From 04e482bd22029710fd512bfd7916781cd5c79495 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Fri, 8 Mar 2024 06:52:02 -0800 Subject: [PATCH 01/42] Fix Esky ES840 decoding --- src/main/java/org/traccar/protocol/EskyProtocolDecoder.java | 2 +- .../java/org/traccar/protocol/EskyProtocolDecoderTest.java | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/traccar/protocol/EskyProtocolDecoder.java b/src/main/java/org/traccar/protocol/EskyProtocolDecoder.java index 4239022d0b5..14b6aaa5acc 100644 --- a/src/main/java/org/traccar/protocol/EskyProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/EskyProtocolDecoder.java @@ -49,7 +49,7 @@ public EskyProtocolDecoder(Protocol protocol) { .number("(d+.d+)[+;]") // speed .number("(d+)[+;]") // course .groupBegin() - .text("0x").number("(d+)[+;]") // input + .text("0x").number("(x+)[+;]") // input .number("(d+)[+;]") // message type .number("(d+)[+;]") // odometer .groupEnd("?") diff --git a/src/test/java/org/traccar/protocol/EskyProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EskyProtocolDecoderTest.java index feec3ebfb74..26132eb4ea9 100644 --- a/src/test/java/org/traccar/protocol/EskyProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/EskyProtocolDecoderTest.java @@ -11,6 +11,9 @@ public void testDecode() throws Exception { var decoder = inject(new EskyProtocolDecoder(null)); + verifyPosition(decoder, text( + "ET;1;864431045310325;R;0+240303180628+-33.40958+149.56797+0.00+63+0xb+0+17814846+1168+0+0+WVWZZZ6RZFY242201+135+DTCNULL")); + verifyAttribute(decoder, text( "ET;0;860337031066546;R;9+200717114059+41.32053+19.80761+0.30+0+0x2+8+40381744+0+1409+11"), Position.KEY_BATTERY, 14.09); From 43508913a7f3356324ec5af447980121d9c64dd0 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Fri, 8 Mar 2024 09:38:03 -0800 Subject: [PATCH 02/42] Add Huabao frame encoder --- .../traccar/protocol/HuabaoFrameEncoder.java | 44 +++++++++++++++++++ .../org/traccar/protocol/HuabaoProtocol.java | 3 +- 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/traccar/protocol/HuabaoFrameEncoder.java diff --git a/src/main/java/org/traccar/protocol/HuabaoFrameEncoder.java b/src/main/java/org/traccar/protocol/HuabaoFrameEncoder.java new file mode 100644 index 00000000000..808529a9841 --- /dev/null +++ b/src/main/java/org/traccar/protocol/HuabaoFrameEncoder.java @@ -0,0 +1,44 @@ +/* + * Copyright 2024 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.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; + +public class HuabaoFrameEncoder extends MessageToByteEncoder { + + @Override + protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) { + + while (msg.isReadable()) { + int b = msg.readUnsignedByte(); + switch (b) { + case 0x7d: + out.writeByte(0x7d); + out.writeByte(0x01); + break; + case 0x7e: + out.writeByte(0x7d); + out.writeByte(0x02); + break; + default: + out.writeByte(b); + break; + } + } + } +} diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocol.java b/src/main/java/org/traccar/protocol/HuabaoProtocol.java index fc12d7d7163..575d15b020f 100644 --- a/src/main/java/org/traccar/protocol/HuabaoProtocol.java +++ b/src/main/java/org/traccar/protocol/HuabaoProtocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2024 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. @@ -33,6 +33,7 @@ public HuabaoProtocol(Config config) { addServer(new TrackerServer(config, getName(), false) { @Override protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(new HuabaoFrameEncoder()); pipeline.addLast(new HuabaoFrameDecoder()); pipeline.addLast(new HuabaoProtocolEncoder(HuabaoProtocol.this)); pipeline.addLast(new HuabaoProtocolDecoder(HuabaoProtocol.this)); From 165a6c76e8127bae7bdf16a9e9a4a24247f1dec4 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Fri, 8 Mar 2024 21:26:09 -0800 Subject: [PATCH 03/42] Fix Huabao frame encoder --- .../traccar/protocol/HuabaoFrameEncoder.java | 22 +++++++++---------- .../protocol/HuabaoFrameEncoderTest.java | 21 ++++++++++++++++++ 2 files changed, 31 insertions(+), 12 deletions(-) create mode 100644 src/test/java/org/traccar/protocol/HuabaoFrameEncoderTest.java diff --git a/src/main/java/org/traccar/protocol/HuabaoFrameEncoder.java b/src/main/java/org/traccar/protocol/HuabaoFrameEncoder.java index 808529a9841..5e48a466add 100644 --- a/src/main/java/org/traccar/protocol/HuabaoFrameEncoder.java +++ b/src/main/java/org/traccar/protocol/HuabaoFrameEncoder.java @@ -24,20 +24,18 @@ public class HuabaoFrameEncoder extends MessageToByteEncoder { @Override protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) { + int startIndex = msg.readerIndex(); while (msg.isReadable()) { + int index = msg.readerIndex(); int b = msg.readUnsignedByte(); - switch (b) { - case 0x7d: - out.writeByte(0x7d); - out.writeByte(0x01); - break; - case 0x7e: - out.writeByte(0x7d); - out.writeByte(0x02); - break; - default: - out.writeByte(b); - break; + if (b == 0x7d) { + out.writeByte(0x7d); + out.writeByte(0x01); + } else if (b == 0x7e && index != startIndex && msg.isReadable()) { + out.writeByte(0x7d); + out.writeByte(0x02); + } else { + out.writeByte(b); } } } diff --git a/src/test/java/org/traccar/protocol/HuabaoFrameEncoderTest.java b/src/test/java/org/traccar/protocol/HuabaoFrameEncoderTest.java new file mode 100644 index 00000000000..a2799e9a8ef --- /dev/null +++ b/src/test/java/org/traccar/protocol/HuabaoFrameEncoderTest.java @@ -0,0 +1,21 @@ +package org.traccar.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; + +public class HuabaoFrameEncoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + var encoder = new HuabaoFrameEncoder(); + + ByteBuf buf = Unpooled.buffer(); + encoder.encode(null, binary("7e307e087d557e"), buf); + verifyFrame(binary("7e307d02087d01557e"), buf); + + } + +} From 823878d4f797cf2fb8e2c6e3b466b3316613d886 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 9 Mar 2024 14:27:13 -0800 Subject: [PATCH 04/42] Expand GV355CEU CAN support --- .../protocol/Gl200TextProtocolDecoder.java | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java index 2e5ffa8d683..7a8d67cf920 100644 --- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java @@ -468,7 +468,6 @@ private Object decodeCan(Channel channel, SocketAddress remoteAddress, String[] index += 1; // report type index += 1; // can bus state long reportMask = Long.parseLong(v[index++], 16); - long reportMaskExt = 0; if (BitUtil.check(reportMask, 0)) { position.set(Position.KEY_VIN, v[index++]); @@ -572,6 +571,8 @@ private Object decodeCan(Channel channel, SocketAddress remoteAddress, String[] index += 1; // electric report mask } } + + long reportMaskExt = 0; if (BitUtil.check(reportMask, 29) && !v[index++].isEmpty()) { reportMaskExt = Long.parseLong(v[index - 1], 16); } @@ -647,6 +648,41 @@ private Object decodeCan(Channel channel, SocketAddress remoteAddress, String[] if (BitUtil.check(reportMaskExt, 23)) { index += 1; // engine torque } + if (BitUtil.check(reportMaskExt, 24)) { + index += 1; // service distance + } + if (BitUtil.check(reportMaskExt, 25)) { + index += 1; // ambient temperature + } + if (BitUtil.check(reportMaskExt, 26)) { + index += 1; // tachograph driver1 working time mask + } + if (BitUtil.check(reportMaskExt, 27)) { + index += 1; // tachograph driver2 working time mask + } + if (BitUtil.check(reportMaskExt, 28)) { + index += 1; // dtc codes + } + if (BitUtil.check(reportMaskExt, 29)) { + index += 1; // gaseous fuel level + } + if (BitUtil.check(reportMaskExt, 30)) { + index += 1; // tachograph information expand + } + + long reportMaskCan = 0; + if (BitUtil.check(reportMaskExt, 31) && !v[index++].isEmpty()) { + reportMaskCan = Long.parseLong(v[index - 1], 16); + } + if (BitUtil.check(reportMaskCan, 0)) { + index += 1; // retarder usage + } + if (BitUtil.check(reportMaskCan, 1)) { + index += 1; // power mode + } + if (BitUtil.check(reportMaskCan, 2)) { + index += 1; // tachograph timestamp + } DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); From 34857cb82abb413100fa4aed668cdca7761e54c4 Mon Sep 17 00:00:00 2001 From: Supriyo Date: Mon, 11 Mar 2024 13:09:38 +0530 Subject: [PATCH 05/42] feat: add configuration for event forwarding with mqtt --- src/main/java/org/traccar/MainModule.java | 14 +--- .../traccar/forward/EventForwarderMqtt.java | 70 +++------------- .../forward/PositionForwarderMqtt.java | 55 ++++++++++++ .../java/org/traccar/helper/MqttUtil.java | 83 +++++++++++++++++++ 4 files changed, 152 insertions(+), 70 deletions(-) create mode 100644 src/main/java/org/traccar/forward/PositionForwarderMqtt.java create mode 100644 src/main/java/org/traccar/helper/MqttUtil.java diff --git a/src/main/java/org/traccar/MainModule.java b/src/main/java/org/traccar/MainModule.java index 3fec4d1e66a..c3c3586be7c 100644 --- a/src/main/java/org/traccar/MainModule.java +++ b/src/main/java/org/traccar/MainModule.java @@ -35,17 +35,7 @@ import org.traccar.database.LdapProvider; import org.traccar.database.OpenIdProvider; import org.traccar.database.StatisticsManager; -import org.traccar.forward.EventForwarder; -import org.traccar.forward.EventForwarderJson; -import org.traccar.forward.EventForwarderAmqp; -import org.traccar.forward.EventForwarderKafka; -import org.traccar.forward.EventForwarderMqtt; -import org.traccar.forward.PositionForwarder; -import org.traccar.forward.PositionForwarderJson; -import org.traccar.forward.PositionForwarderAmqp; -import org.traccar.forward.PositionForwarderKafka; -import org.traccar.forward.PositionForwarderRedis; -import org.traccar.forward.PositionForwarderUrl; +import org.traccar.forward.*; import org.traccar.geocoder.AddressFormat; import org.traccar.geocoder.BanGeocoder; import org.traccar.geocoder.BingMapsGeocoder; @@ -386,6 +376,8 @@ public static PositionForwarder providePositionForwarder(Config config, Client c return new PositionForwarderAmqp(config, objectMapper); case "kafka": return new PositionForwarderKafka(config, objectMapper); + case "mqtt": + return new PositionForwarderMqtt(config, objectMapper); case "redis": return new PositionForwarderRedis(config, objectMapper); case "url": diff --git a/src/main/java/org/traccar/forward/EventForwarderMqtt.java b/src/main/java/org/traccar/forward/EventForwarderMqtt.java index 7f4e29384b6..90e8500eaee 100644 --- a/src/main/java/org/traccar/forward/EventForwarderMqtt.java +++ b/src/main/java/org/traccar/forward/EventForwarderMqtt.java @@ -15,86 +15,38 @@ */ package org.traccar.forward; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.hivemq.client.mqtt.datatypes.MqttQos; -import com.hivemq.client.mqtt.mqtt5.Mqtt5AsyncClient; -import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; -import com.hivemq.client.mqtt.mqtt5.message.auth.Mqtt5SimpleAuth; import org.traccar.config.Config; import org.traccar.config.Keys; +import org.traccar.helper.MqttUtil; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.UUID; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.hivemq.client.mqtt.mqtt3.Mqtt3AsyncClient; public class EventForwarderMqtt implements EventForwarder { - private final Mqtt5AsyncClient client; + private final Mqtt3AsyncClient client; private final ObjectMapper objectMapper; private final String topic; public EventForwarderMqtt(Config config, ObjectMapper objectMapper) { - URI url; - try { - url = new URI(config.getString(Keys.EVENT_FORWARD_URL)); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - - String userInfo = url.getUserInfo(); - Mqtt5SimpleAuth simpleAuth = null; - if (userInfo != null) { - int delimiter = userInfo.indexOf(':'); - if (delimiter == -1) { - throw new IllegalArgumentException("Wrong credentials. Should be in format \"username:password\""); - } else { - simpleAuth = Mqtt5SimpleAuth.builder() - .username(userInfo.substring(0, delimiter++)) - .password(userInfo.substring(delimiter).getBytes()) - .build(); - } - } - - String host = url.getHost(); - int port = url.getPort(); - client = Mqtt5Client.builder() - .identifier("traccar-" + UUID.randomUUID()) - .serverHost(host) - .serverPort(port) - .simpleAuth(simpleAuth) - .automaticReconnectWithDefaultConfig() - .buildAsync(); - - client.connectWith() - .send() - .whenComplete((message, e) -> { - if (e != null) { - throw new RuntimeException(e); - } - }); - + this.topic = config.getString(Keys.EVENT_FORWARD_TOPIC); + client = MqttUtil.createClient(config.getString(Keys.EVENT_FORWARD_URL)); this.objectMapper = objectMapper; - topic = config.getString(Keys.EVENT_FORWARD_TOPIC); } @Override public void forward(EventData eventData, ResultHandler resultHandler) { - byte[] payload; + String payload; try { - payload = objectMapper.writeValueAsString(eventData).getBytes(); + payload = objectMapper.writeValueAsString(eventData); } catch (JsonProcessingException e) { resultHandler.onResult(false, e); return; } - client.publishWith() - .topic(topic) - .qos(MqttQos.AT_LEAST_ONCE) - .payload(payload) - .send() - .whenComplete((message, e) -> resultHandler.onResult(e == null, e)); + MqttUtil.publish(client, topic, payload, (message, e) -> resultHandler.onResult(e == null, e)); } -} +} \ No newline at end of file diff --git a/src/main/java/org/traccar/forward/PositionForwarderMqtt.java b/src/main/java/org/traccar/forward/PositionForwarderMqtt.java new file mode 100644 index 00000000000..e530c76a18f --- /dev/null +++ b/src/main/java/org/traccar/forward/PositionForwarderMqtt.java @@ -0,0 +1,55 @@ +/* + * Copyright 2022 - 2023 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.forward; + +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.helper.MqttUtil; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.hivemq.client.mqtt.mqtt3.Mqtt3AsyncClient; + +public class PositionForwarderMqtt implements PositionForwarder { + private final Mqtt3AsyncClient client; + + @Override + public void forward(final PositionData positionData, final ResultHandler resultHandler) { + publish(topic, positionData, resultHandler); + } + private final ObjectMapper objectMapper; + + protected final String topic; + + + public PositionForwarderMqtt(final Config config, final ObjectMapper objectMapper) { + this.topic = config.getString(Keys.FORWARD_TOPIC); + this.client = MqttUtil.createClient(config.getString(Keys.FORWARD_URL)); + this.objectMapper = objectMapper; + } + + protected void publish(final String pubTopic, final Object object, final ResultHandler resultHandler) { + final String payload; + try { + payload = objectMapper.writeValueAsString(object); + } catch (JsonProcessingException e) { + resultHandler.onResult(false, e); + return; + } + MqttUtil.publish(client, pubTopic, payload, (message, e) -> resultHandler.onResult(e == null, e)); + } + +} \ No newline at end of file diff --git a/src/main/java/org/traccar/helper/MqttUtil.java b/src/main/java/org/traccar/helper/MqttUtil.java new file mode 100644 index 00000000000..54167f65c7c --- /dev/null +++ b/src/main/java/org/traccar/helper/MqttUtil.java @@ -0,0 +1,83 @@ +/* + * Copyright 2023 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.helper; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.UUID; +import java.util.function.BiConsumer; + +import com.hivemq.client.mqtt.datatypes.MqttQos; +import com.hivemq.client.mqtt.mqtt3.Mqtt3AsyncClient; +import com.hivemq.client.mqtt.mqtt3.Mqtt3Client; +import com.hivemq.client.mqtt.mqtt3.Mqtt3ClientBuilder; +import com.hivemq.client.mqtt.mqtt3.message.auth.Mqtt3SimpleAuth; +import com.hivemq.client.mqtt.mqtt3.message.publish.Mqtt3Publish; + +public final class MqttUtil { + + private MqttUtil() { + + } + + public static Mqtt3AsyncClient createClient(final String url) { + URI uri; + try { + uri = new URI(url); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + + final Mqtt3SimpleAuth simpleAuth = getSimpleAuth(uri); + + final String host = uri.getHost(); + final int port = uri.getPort(); + final Mqtt3ClientBuilder builder = Mqtt3Client.builder().identifier("traccar-" + UUID.randomUUID()) + .serverHost(host).serverPort(port).simpleAuth(simpleAuth).automaticReconnectWithDefaultConfig(); + final Mqtt3AsyncClient client; + + client = builder.buildAsync(); + + + client.connectWith().send().whenComplete((message, e) -> { + throw new RuntimeException(e); + }); + + return client; + } + + private static Mqtt3SimpleAuth getSimpleAuth(final URI uri) { + final String userInfo = uri.getUserInfo(); + Mqtt3SimpleAuth simpleAuth = null; + if (userInfo != null) { + int delimiter = userInfo.indexOf(':'); + if (delimiter == -1) { + throw new IllegalArgumentException("Wrong MQTT credentials. Should be in format \"username:password\""); + } else { + simpleAuth = Mqtt3SimpleAuth.builder().username(userInfo.substring(0, delimiter++)) + .password(userInfo.substring(delimiter).getBytes()).build(); + } + } + return simpleAuth; + } + + public static void publish(final Mqtt3AsyncClient client, final String pubTopic, final String payload, + final BiConsumer whenComplete) { + client.publishWith().topic(pubTopic).qos(MqttQos.AT_LEAST_ONCE).payload(payload.getBytes()).send() + .whenComplete(whenComplete); + } + +} \ No newline at end of file From 87df697b992234caa6e89229dd7fe1ee194859f5 Mon Sep 17 00:00:00 2001 From: Supriyo Date: Tue, 12 Mar 2024 00:08:04 +0530 Subject: [PATCH 06/42] linting changes and upgrade mqqt3 to mqtt5 --- src/main/java/org/traccar/MainModule.java | 13 +++++- .../traccar/forward/EventForwarderMqtt.java | 6 +-- .../forward/PositionForwarderMqtt.java | 6 +-- .../java/org/traccar/helper/MqttUtil.java | 40 +++++++++---------- 4 files changed, 37 insertions(+), 28 deletions(-) diff --git a/src/main/java/org/traccar/MainModule.java b/src/main/java/org/traccar/MainModule.java index c3c3586be7c..d5b7f7d7533 100644 --- a/src/main/java/org/traccar/MainModule.java +++ b/src/main/java/org/traccar/MainModule.java @@ -35,7 +35,18 @@ import org.traccar.database.LdapProvider; import org.traccar.database.OpenIdProvider; import org.traccar.database.StatisticsManager; -import org.traccar.forward.*; +import org.traccar.forward.EventForwarder; +import org.traccar.forward.EventForwarderJson; +import org.traccar.forward.EventForwarderAmqp; +import org.traccar.forward.EventForwarderKafka; +import org.traccar.forward.EventForwarderMqtt; +import org.traccar.forward.PositionForwarder; +import org.traccar.forward.PositionForwarderJson; +import org.traccar.forward.PositionForwarderAmqp; +import org.traccar.forward.PositionForwarderKafka; +import org.traccar.forward.PositionForwarderRedis; +import org.traccar.forward.PositionForwarderUrl; +import org.traccar.forward.PositionForwarderMqtt; import org.traccar.geocoder.AddressFormat; import org.traccar.geocoder.BanGeocoder; import org.traccar.geocoder.BingMapsGeocoder; diff --git a/src/main/java/org/traccar/forward/EventForwarderMqtt.java b/src/main/java/org/traccar/forward/EventForwarderMqtt.java index 90e8500eaee..83df795b153 100644 --- a/src/main/java/org/traccar/forward/EventForwarderMqtt.java +++ b/src/main/java/org/traccar/forward/EventForwarderMqtt.java @@ -21,11 +21,11 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.hivemq.client.mqtt.mqtt3.Mqtt3AsyncClient; +import com.hivemq.client.mqtt.mqtt5.Mqtt5AsyncClient; public class EventForwarderMqtt implements EventForwarder { - private final Mqtt3AsyncClient client; + private final Mqtt5AsyncClient client; private final ObjectMapper objectMapper; private final String topic; @@ -49,4 +49,4 @@ public void forward(EventData eventData, ResultHandler resultHandler) { MqttUtil.publish(client, topic, payload, (message, e) -> resultHandler.onResult(e == null, e)); } -} \ No newline at end of file +} diff --git a/src/main/java/org/traccar/forward/PositionForwarderMqtt.java b/src/main/java/org/traccar/forward/PositionForwarderMqtt.java index e530c76a18f..a6d0965b8a9 100644 --- a/src/main/java/org/traccar/forward/PositionForwarderMqtt.java +++ b/src/main/java/org/traccar/forward/PositionForwarderMqtt.java @@ -21,10 +21,10 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.hivemq.client.mqtt.mqtt3.Mqtt3AsyncClient; +import com.hivemq.client.mqtt.mqtt5.Mqtt5AsyncClient; public class PositionForwarderMqtt implements PositionForwarder { - private final Mqtt3AsyncClient client; + private final Mqtt5AsyncClient client; @Override public void forward(final PositionData positionData, final ResultHandler resultHandler) { @@ -52,4 +52,4 @@ protected void publish(final String pubTopic, final Object object, final ResultH MqttUtil.publish(client, pubTopic, payload, (message, e) -> resultHandler.onResult(e == null, e)); } -} \ No newline at end of file +} diff --git a/src/main/java/org/traccar/helper/MqttUtil.java b/src/main/java/org/traccar/helper/MqttUtil.java index 54167f65c7c..9fc16012f78 100644 --- a/src/main/java/org/traccar/helper/MqttUtil.java +++ b/src/main/java/org/traccar/helper/MqttUtil.java @@ -21,11 +21,11 @@ import java.util.function.BiConsumer; import com.hivemq.client.mqtt.datatypes.MqttQos; -import com.hivemq.client.mqtt.mqtt3.Mqtt3AsyncClient; -import com.hivemq.client.mqtt.mqtt3.Mqtt3Client; -import com.hivemq.client.mqtt.mqtt3.Mqtt3ClientBuilder; -import com.hivemq.client.mqtt.mqtt3.message.auth.Mqtt3SimpleAuth; -import com.hivemq.client.mqtt.mqtt3.message.publish.Mqtt3Publish; +import com.hivemq.client.mqtt.mqtt5.Mqtt5AsyncClient; +import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; +import com.hivemq.client.mqtt.mqtt5.Mqtt5ClientBuilder; +import com.hivemq.client.mqtt.mqtt5.message.auth.Mqtt5SimpleAuth; +import com.hivemq.client.mqtt.mqtt5.message.publish.Mqtt5PublishResult; public final class MqttUtil { @@ -33,7 +33,7 @@ private MqttUtil() { } - public static Mqtt3AsyncClient createClient(final String url) { + public static Mqtt5AsyncClient createClient(final String url) { URI uri; try { uri = new URI(url); @@ -41,17 +41,14 @@ public static Mqtt3AsyncClient createClient(final String url) { throw new RuntimeException(e); } - final Mqtt3SimpleAuth simpleAuth = getSimpleAuth(uri); + Mqtt5SimpleAuth simpleAuth = getSimpleAuth(uri); - final String host = uri.getHost(); - final int port = uri.getPort(); - final Mqtt3ClientBuilder builder = Mqtt3Client.builder().identifier("traccar-" + UUID.randomUUID()) + String host = uri.getHost(); + int port = uri.getPort(); + Mqtt5ClientBuilder builder = Mqtt5Client.builder().identifier("traccar-" + UUID.randomUUID()) .serverHost(host).serverPort(port).simpleAuth(simpleAuth).automaticReconnectWithDefaultConfig(); - final Mqtt3AsyncClient client; - + Mqtt5AsyncClient client; client = builder.buildAsync(); - - client.connectWith().send().whenComplete((message, e) -> { throw new RuntimeException(e); }); @@ -59,25 +56,26 @@ public static Mqtt3AsyncClient createClient(final String url) { return client; } - private static Mqtt3SimpleAuth getSimpleAuth(final URI uri) { - final String userInfo = uri.getUserInfo(); - Mqtt3SimpleAuth simpleAuth = null; + private static Mqtt5SimpleAuth getSimpleAuth(final URI uri) { + String userInfo = uri.getUserInfo(); + Mqtt5SimpleAuth simpleAuth = null; if (userInfo != null) { int delimiter = userInfo.indexOf(':'); if (delimiter == -1) { throw new IllegalArgumentException("Wrong MQTT credentials. Should be in format \"username:password\""); } else { - simpleAuth = Mqtt3SimpleAuth.builder().username(userInfo.substring(0, delimiter++)) + simpleAuth = Mqtt5SimpleAuth.builder().username(userInfo.substring(0, delimiter++)) .password(userInfo.substring(delimiter).getBytes()).build(); } } return simpleAuth; } - public static void publish(final Mqtt3AsyncClient client, final String pubTopic, final String payload, - final BiConsumer whenComplete) { + public static void publish(final Mqtt5AsyncClient client, final String pubTopic, final String payload, + final BiConsumer whenComplete) { client.publishWith().topic(pubTopic).qos(MqttQos.AT_LEAST_ONCE).payload(payload.getBytes()).send() .whenComplete(whenComplete); } -} \ No newline at end of file +} From 75542501573cedb0b7d34a65df5ca650faf82d24 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Wed, 13 Mar 2024 17:43:15 -0700 Subject: [PATCH 07/42] Remove Mozilla location service --- src/main/java/org/traccar/MainModule.java | 7 ++--- src/main/java/org/traccar/config/Keys.java | 2 +- .../MozillaGeolocationProvider.java | 28 ------------------- .../geolocation/GeolocationProviderTest.java | 2 +- 4 files changed, 4 insertions(+), 35 deletions(-) delete mode 100644 src/main/java/org/traccar/geolocation/MozillaGeolocationProvider.java diff --git a/src/main/java/org/traccar/MainModule.java b/src/main/java/org/traccar/MainModule.java index 3fec4d1e66a..acdf48874ad 100644 --- a/src/main/java/org/traccar/MainModule.java +++ b/src/main/java/org/traccar/MainModule.java @@ -69,7 +69,6 @@ import org.traccar.geocoder.TomTomGeocoder; import org.traccar.geolocation.GeolocationProvider; import org.traccar.geolocation.GoogleGeolocationProvider; -import org.traccar.geolocation.MozillaGeolocationProvider; import org.traccar.geolocation.OpenCellIdGeolocationProvider; import org.traccar.geolocation.UnwiredGeolocationProvider; import org.traccar.handler.GeocoderHandler; @@ -276,18 +275,16 @@ public static Geocoder provideGeocoder(Config config, Client client, StatisticsM @Provides public static GeolocationProvider provideGeolocationProvider(Config config, Client client) { if (config.getBoolean(Keys.GEOLOCATION_ENABLE)) { - String type = config.getString(Keys.GEOLOCATION_TYPE, "mozilla"); + String type = config.getString(Keys.GEOLOCATION_TYPE, "google"); String url = config.getString(Keys.GEOLOCATION_URL); String key = config.getString(Keys.GEOLOCATION_KEY); switch (type) { - case "google": - return new GoogleGeolocationProvider(client, key); case "opencellid": return new OpenCellIdGeolocationProvider(client, url, key); case "unwired": return new UnwiredGeolocationProvider(client, url, key); default: - return new MozillaGeolocationProvider(client, key); + return new GoogleGeolocationProvider(client, key); } } return null; diff --git a/src/main/java/org/traccar/config/Keys.java b/src/main/java/org/traccar/config/Keys.java index 02e6848759a..4aacb2cd860 100644 --- a/src/main/java/org/traccar/config/Keys.java +++ b/src/main/java/org/traccar/config/Keys.java @@ -1656,7 +1656,7 @@ private Keys() { List.of(KeyType.CONFIG)); /** - * Provider to use for LBS location. Available options: google, mozilla and opencellid. By default opencellid is + * Provider to use for LBS location. Available options: google, unwired and opencellid. By default, google is * used. You have to supply a key that you get from corresponding provider. For more information see LBS geolocation * documentation. */ diff --git a/src/main/java/org/traccar/geolocation/MozillaGeolocationProvider.java b/src/main/java/org/traccar/geolocation/MozillaGeolocationProvider.java deleted file mode 100644 index 7eb22dccad2..00000000000 --- a/src/main/java/org/traccar/geolocation/MozillaGeolocationProvider.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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. - * 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.geolocation; - -import jakarta.ws.rs.client.Client; - -public class MozillaGeolocationProvider extends UniversalGeolocationProvider { - - private static final String URL = "https://location.services.mozilla.com/v1/geolocate"; - - public MozillaGeolocationProvider(Client client, String key) { - super(client, URL, key != null ? key : "test"); - } - -} diff --git a/src/test/java/org/traccar/geolocation/GeolocationProviderTest.java b/src/test/java/org/traccar/geolocation/GeolocationProviderTest.java index da5ae334046..c1fa6dbbed3 100644 --- a/src/test/java/org/traccar/geolocation/GeolocationProviderTest.java +++ b/src/test/java/org/traccar/geolocation/GeolocationProviderTest.java @@ -19,7 +19,7 @@ public class GeolocationProviderTest extends BaseTest { @Disabled @Test public void testMozilla() throws Exception { - MozillaGeolocationProvider provider = new MozillaGeolocationProvider(client, null); + GoogleGeolocationProvider provider = new GoogleGeolocationProvider(client, null); Network network = new Network(CellTower.from(208, 1, 2, 1234567)); From 73dffe380b1faa799f6944d730e71b1b85633f76 Mon Sep 17 00:00:00 2001 From: Sahal Ghafur Date: Fri, 15 Mar 2024 11:16:57 +0000 Subject: [PATCH 08/42] Update SPO2 Key for Minifinder2 --- src/main/java/org/traccar/model/Position.java | 1 + .../org/traccar/protocol/Minifinder2ProtocolDecoder.java | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/src/main/java/org/traccar/model/Position.java b/src/main/java/org/traccar/model/Position.java index 39f63217db3..ac028917b43 100644 --- a/src/main/java/org/traccar/model/Position.java +++ b/src/main/java/org/traccar/model/Position.java @@ -45,6 +45,7 @@ public class Position extends Message { public static final String KEY_HOURS = "hours"; // milliseconds public static final String KEY_STEPS = "steps"; public static final String KEY_HEART_RATE = "heartRate"; + public static final String KEY_SP02 = "spO2"; public static final String KEY_INPUT = "input"; public static final String KEY_OUTPUT = "output"; public static final String KEY_IMAGE = "image"; diff --git a/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java index 64373e3446d..e50655b37b4 100644 --- a/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java @@ -295,6 +295,13 @@ protected Object decode( position.set(Position.KEY_HEART_RATE, heartRate); } break; + case 0x41: + buf.readUnsignedIntLE(); // timestamp + int spO2 = buf.readUnsignedByte(); + if (spO2 > 1) { + position.set(Position.KEY_SP02, spO2); + } + break; default: break; } From a9b08325240b3f4c76d5f6a042c6740b1f81a889 Mon Sep 17 00:00:00 2001 From: Sahal Ghafur Date: Fri, 15 Mar 2024 13:28:23 +0000 Subject: [PATCH 09/42] Updated Position Key for spO2 --- src/main/java/org/traccar/model/Position.java | 1 - .../java/org/traccar/protocol/Minifinder2ProtocolDecoder.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/traccar/model/Position.java b/src/main/java/org/traccar/model/Position.java index ac028917b43..39f63217db3 100644 --- a/src/main/java/org/traccar/model/Position.java +++ b/src/main/java/org/traccar/model/Position.java @@ -45,7 +45,6 @@ public class Position extends Message { public static final String KEY_HOURS = "hours"; // milliseconds public static final String KEY_STEPS = "steps"; public static final String KEY_HEART_RATE = "heartRate"; - public static final String KEY_SP02 = "spO2"; public static final String KEY_INPUT = "input"; public static final String KEY_OUTPUT = "output"; public static final String KEY_IMAGE = "image"; diff --git a/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java index e50655b37b4..2d6bde89f26 100644 --- a/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java @@ -299,7 +299,7 @@ protected Object decode( buf.readUnsignedIntLE(); // timestamp int spO2 = buf.readUnsignedByte(); if (spO2 > 1) { - position.set(Position.KEY_SP02, spO2); + position.set("spO2", spO2); } break; default: From 22b0611d9c00e70f4b70787e314075afcd4538ec Mon Sep 17 00:00:00 2001 From: Supriyo Date: Sat, 16 Mar 2024 12:36:48 +0530 Subject: [PATCH 10/42] feat: refractor mqtt utils to MqttClient like EmqxClient --- .../traccar/forward/EventForwarderMqtt.java | 10 +++---- .../MqttUtil.java => forward/MqttClient.java} | 27 ++++++++----------- .../forward/PositionForwarderMqtt.java | 9 +++---- 3 files changed, 17 insertions(+), 29 deletions(-) rename src/main/java/org/traccar/{helper/MqttUtil.java => forward/MqttClient.java} (81%) diff --git a/src/main/java/org/traccar/forward/EventForwarderMqtt.java b/src/main/java/org/traccar/forward/EventForwarderMqtt.java index 83df795b153..7d1c7dd3ca6 100644 --- a/src/main/java/org/traccar/forward/EventForwarderMqtt.java +++ b/src/main/java/org/traccar/forward/EventForwarderMqtt.java @@ -17,22 +17,20 @@ import org.traccar.config.Config; import org.traccar.config.Keys; -import org.traccar.helper.MqttUtil; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.hivemq.client.mqtt.mqtt5.Mqtt5AsyncClient; public class EventForwarderMqtt implements EventForwarder { - private final Mqtt5AsyncClient client; + private final MqttClient mqttClient; private final ObjectMapper objectMapper; private final String topic; public EventForwarderMqtt(Config config, ObjectMapper objectMapper) { this.topic = config.getString(Keys.EVENT_FORWARD_TOPIC); - client = MqttUtil.createClient(config.getString(Keys.EVENT_FORWARD_URL)); + mqttClient = new MqttClient(config.getString(Keys.FORWARD_URL)); this.objectMapper = objectMapper; } @@ -41,12 +39,10 @@ public void forward(EventData eventData, ResultHandler resultHandler) { String payload; try { payload = objectMapper.writeValueAsString(eventData); + mqttClient.publish(topic, payload, (message, e) -> resultHandler.onResult(e == null, e)); } catch (JsonProcessingException e) { resultHandler.onResult(false, e); - return; } - - MqttUtil.publish(client, topic, payload, (message, e) -> resultHandler.onResult(e == null, e)); } } diff --git a/src/main/java/org/traccar/helper/MqttUtil.java b/src/main/java/org/traccar/forward/MqttClient.java similarity index 81% rename from src/main/java/org/traccar/helper/MqttUtil.java rename to src/main/java/org/traccar/forward/MqttClient.java index 9fc16012f78..9059fc876e0 100644 --- a/src/main/java/org/traccar/helper/MqttUtil.java +++ b/src/main/java/org/traccar/forward/MqttClient.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.traccar.helper; +package org.traccar.forward; import java.net.URI; import java.net.URISyntaxException; @@ -27,13 +27,9 @@ import com.hivemq.client.mqtt.mqtt5.message.auth.Mqtt5SimpleAuth; import com.hivemq.client.mqtt.mqtt5.message.publish.Mqtt5PublishResult; -public final class MqttUtil { - - private MqttUtil() { - - } - - public static Mqtt5AsyncClient createClient(final String url) { +public class MqttClient { + private final Mqtt5AsyncClient client; + MqttClient(String url) { URI uri; try { uri = new URI(url); @@ -41,22 +37,21 @@ public static Mqtt5AsyncClient createClient(final String url) { throw new RuntimeException(e); } - Mqtt5SimpleAuth simpleAuth = getSimpleAuth(uri); + Mqtt5SimpleAuth simpleAuth = this.getSimpleAuth(uri); String host = uri.getHost(); int port = uri.getPort(); Mqtt5ClientBuilder builder = Mqtt5Client.builder().identifier("traccar-" + UUID.randomUUID()) .serverHost(host).serverPort(port).simpleAuth(simpleAuth).automaticReconnectWithDefaultConfig(); - Mqtt5AsyncClient client; + client = builder.buildAsync(); client.connectWith().send().whenComplete((message, e) -> { throw new RuntimeException(e); }); - - return client; } - private static Mqtt5SimpleAuth getSimpleAuth(final URI uri) { + + private Mqtt5SimpleAuth getSimpleAuth(final URI uri) { String userInfo = uri.getUserInfo(); Mqtt5SimpleAuth simpleAuth = null; if (userInfo != null) { @@ -71,9 +66,9 @@ private static Mqtt5SimpleAuth getSimpleAuth(final URI uri) { return simpleAuth; } - public static void publish(final Mqtt5AsyncClient client, final String pubTopic, final String payload, - final BiConsumer whenComplete) { + public void publish(final String pubTopic, final String payload, + final BiConsumer whenComplete) { client.publishWith().topic(pubTopic).qos(MqttQos.AT_LEAST_ONCE).payload(payload.getBytes()).send() .whenComplete(whenComplete); } diff --git a/src/main/java/org/traccar/forward/PositionForwarderMqtt.java b/src/main/java/org/traccar/forward/PositionForwarderMqtt.java index a6d0965b8a9..a22c3bee6e9 100644 --- a/src/main/java/org/traccar/forward/PositionForwarderMqtt.java +++ b/src/main/java/org/traccar/forward/PositionForwarderMqtt.java @@ -17,14 +17,12 @@ import org.traccar.config.Config; import org.traccar.config.Keys; -import org.traccar.helper.MqttUtil; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.hivemq.client.mqtt.mqtt5.Mqtt5AsyncClient; public class PositionForwarderMqtt implements PositionForwarder { - private final Mqtt5AsyncClient client; + private final MqttClient mqttClient; @Override public void forward(final PositionData positionData, final ResultHandler resultHandler) { @@ -37,7 +35,7 @@ public void forward(final PositionData positionData, final ResultHandler resultH public PositionForwarderMqtt(final Config config, final ObjectMapper objectMapper) { this.topic = config.getString(Keys.FORWARD_TOPIC); - this.client = MqttUtil.createClient(config.getString(Keys.FORWARD_URL)); + mqttClient = new MqttClient(config.getString(Keys.FORWARD_URL)); this.objectMapper = objectMapper; } @@ -45,11 +43,10 @@ protected void publish(final String pubTopic, final Object object, final ResultH final String payload; try { payload = objectMapper.writeValueAsString(object); + mqttClient.publish(pubTopic, payload, (message, e) -> resultHandler.onResult(e == null, e)); } catch (JsonProcessingException e) { resultHandler.onResult(false, e); - return; } - MqttUtil.publish(client, pubTopic, payload, (message, e) -> resultHandler.onResult(e == null, e)); } } From ad1e5c4aba6d97673e7b065b061c8f964aedf447 Mon Sep 17 00:00:00 2001 From: supriyo Date: Sat, 16 Mar 2024 20:03:11 +0530 Subject: [PATCH 11/42] update copyright text. --- src/main/java/org/traccar/forward/MqttClient.java | 2 +- src/main/java/org/traccar/forward/PositionForwarderMqtt.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/traccar/forward/MqttClient.java b/src/main/java/org/traccar/forward/MqttClient.java index 9059fc876e0..ad3065b585f 100644 --- a/src/main/java/org/traccar/forward/MqttClient.java +++ b/src/main/java/org/traccar/forward/MqttClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Anton Tananaev (anton@traccar.org) + * Copyright 2024 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. diff --git a/src/main/java/org/traccar/forward/PositionForwarderMqtt.java b/src/main/java/org/traccar/forward/PositionForwarderMqtt.java index a22c3bee6e9..abbdc538a3e 100644 --- a/src/main/java/org/traccar/forward/PositionForwarderMqtt.java +++ b/src/main/java/org/traccar/forward/PositionForwarderMqtt.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 - 2023 Anton Tananaev (anton@traccar.org) + * Copyright 2024 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. From 40acf3bd81ca74684ebdba57357dc5b112559a69 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 16 Mar 2024 07:43:42 -0700 Subject: [PATCH 12/42] Clean up formatting --- .../org/traccar/forward/EventForwarderMqtt.java | 3 +-- .../java/org/traccar/forward/MqttClient.java | 10 +++++----- .../traccar/forward/PositionForwarderMqtt.java | 17 ++++++----------- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/traccar/forward/EventForwarderMqtt.java b/src/main/java/org/traccar/forward/EventForwarderMqtt.java index 7d1c7dd3ca6..ea075fc6c14 100644 --- a/src/main/java/org/traccar/forward/EventForwarderMqtt.java +++ b/src/main/java/org/traccar/forward/EventForwarderMqtt.java @@ -36,9 +36,8 @@ public EventForwarderMqtt(Config config, ObjectMapper objectMapper) { @Override public void forward(EventData eventData, ResultHandler resultHandler) { - String payload; try { - payload = objectMapper.writeValueAsString(eventData); + String payload = objectMapper.writeValueAsString(eventData); mqttClient.publish(topic, payload, (message, e) -> resultHandler.onResult(e == null, e)); } catch (JsonProcessingException e) { resultHandler.onResult(false, e); diff --git a/src/main/java/org/traccar/forward/MqttClient.java b/src/main/java/org/traccar/forward/MqttClient.java index ad3065b585f..416a167ec4c 100644 --- a/src/main/java/org/traccar/forward/MqttClient.java +++ b/src/main/java/org/traccar/forward/MqttClient.java @@ -28,7 +28,9 @@ import com.hivemq.client.mqtt.mqtt5.message.publish.Mqtt5PublishResult; public class MqttClient { + private final Mqtt5AsyncClient client; + MqttClient(String url) { URI uri; try { @@ -50,8 +52,7 @@ public class MqttClient { }); } - - private Mqtt5SimpleAuth getSimpleAuth(final URI uri) { + private Mqtt5SimpleAuth getSimpleAuth(URI uri) { String userInfo = uri.getUserInfo(); Mqtt5SimpleAuth simpleAuth = null; if (userInfo != null) { @@ -66,9 +67,8 @@ private Mqtt5SimpleAuth getSimpleAuth(final URI uri) { return simpleAuth; } - public void publish(final String pubTopic, final String payload, - final BiConsumer whenComplete) { + public void publish( + String pubTopic, String payload, BiConsumer whenComplete) { client.publishWith().topic(pubTopic).qos(MqttQos.AT_LEAST_ONCE).payload(payload.getBytes()).send() .whenComplete(whenComplete); } diff --git a/src/main/java/org/traccar/forward/PositionForwarderMqtt.java b/src/main/java/org/traccar/forward/PositionForwarderMqtt.java index abbdc538a3e..53f0ced19ba 100644 --- a/src/main/java/org/traccar/forward/PositionForwarderMqtt.java +++ b/src/main/java/org/traccar/forward/PositionForwarderMqtt.java @@ -22,16 +22,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; public class PositionForwarderMqtt implements PositionForwarder { - private final MqttClient mqttClient; - @Override - public void forward(final PositionData positionData, final ResultHandler resultHandler) { - publish(topic, positionData, resultHandler); - } + private final MqttClient mqttClient; private final ObjectMapper objectMapper; - protected final String topic; - + private final String topic; public PositionForwarderMqtt(final Config config, final ObjectMapper objectMapper) { this.topic = config.getString(Keys.FORWARD_TOPIC); @@ -39,11 +34,11 @@ public PositionForwarderMqtt(final Config config, final ObjectMapper objectMappe this.objectMapper = objectMapper; } - protected void publish(final String pubTopic, final Object object, final ResultHandler resultHandler) { - final String payload; + @Override + public void forward(PositionData positionData, ResultHandler resultHandler) { try { - payload = objectMapper.writeValueAsString(object); - mqttClient.publish(pubTopic, payload, (message, e) -> resultHandler.onResult(e == null, e)); + String payload = objectMapper.writeValueAsString(topic); + mqttClient.publish(topic, payload, (message, e) -> resultHandler.onResult(e == null, e)); } catch (JsonProcessingException e) { resultHandler.onResult(false, e); } From d66174fc5ee33998b5b3960336a47de3d2f00196 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 16 Mar 2024 07:45:22 -0700 Subject: [PATCH 13/42] Fix forwarding URL --- src/main/java/org/traccar/forward/EventForwarderMqtt.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/traccar/forward/EventForwarderMqtt.java b/src/main/java/org/traccar/forward/EventForwarderMqtt.java index ea075fc6c14..7fee495ac71 100644 --- a/src/main/java/org/traccar/forward/EventForwarderMqtt.java +++ b/src/main/java/org/traccar/forward/EventForwarderMqtt.java @@ -30,7 +30,7 @@ public class EventForwarderMqtt implements EventForwarder { public EventForwarderMqtt(Config config, ObjectMapper objectMapper) { this.topic = config.getString(Keys.EVENT_FORWARD_TOPIC); - mqttClient = new MqttClient(config.getString(Keys.FORWARD_URL)); + mqttClient = new MqttClient(config.getString(Keys.EVENT_FORWARD_URL)); this.objectMapper = objectMapper; } From 5b8b40faebf6996f1d2f5aef4c733dfe13d73b1b Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 16 Mar 2024 19:46:44 -0700 Subject: [PATCH 14/42] Add local address to connection key --- .../handler/StandardLoggingHandler.java | 13 ++--- .../java/org/traccar/model/LogRecord.java | 21 +++++--- .../org/traccar/session/ConnectionKey.java | 54 +++++++++++++++++++ .../traccar/session/ConnectionManager.java | 28 +++++----- .../org/traccar/session/DeviceSession.java | 6 +-- 5 files changed, 92 insertions(+), 30 deletions(-) create mode 100644 src/main/java/org/traccar/session/ConnectionKey.java diff --git a/src/main/java/org/traccar/handler/StandardLoggingHandler.java b/src/main/java/org/traccar/handler/StandardLoggingHandler.java index 5978d632e4d..513602dd8a9 100644 --- a/src/main/java/org/traccar/handler/StandardLoggingHandler.java +++ b/src/main/java/org/traccar/handler/StandardLoggingHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 - 2023 Anton Tananaev (anton@traccar.org) + * Copyright 2019 - 2024 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. @@ -28,8 +28,6 @@ import org.traccar.model.LogRecord; import org.traccar.session.ConnectionManager; -import java.net.InetSocketAddress; - public class StandardLoggingHandler extends ChannelDuplexHandler { private static final Logger LOGGER = LoggerFactory.getLogger(StandardLoggingHandler.class); @@ -48,7 +46,7 @@ public void setConnectionManager(ConnectionManager connectionManager) { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - LogRecord record = createLogRecord(msg); + LogRecord record = createLogRecord(ctx, msg); log(ctx, false, record); super.channelRead(ctx, msg); if (record != null) { @@ -58,16 +56,15 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { - log(ctx, true, createLogRecord(msg)); + log(ctx, true, createLogRecord(ctx, msg)); super.write(ctx, msg, promise); } - private LogRecord createLogRecord(Object msg) { + private LogRecord createLogRecord(ChannelHandlerContext ctx, Object msg) { if (msg instanceof NetworkMessage) { NetworkMessage networkMessage = (NetworkMessage) msg; if (networkMessage.getMessage() instanceof ByteBuf) { - LogRecord record = new LogRecord(); - record.setAddress((InetSocketAddress) networkMessage.getRemoteAddress()); + LogRecord record = new LogRecord(ctx.channel().localAddress(), networkMessage.getRemoteAddress()); record.setProtocol(protocol); record.setData(ByteBufUtil.hexDump((ByteBuf) networkMessage.getMessage())); return record; diff --git a/src/main/java/org/traccar/model/LogRecord.java b/src/main/java/org/traccar/model/LogRecord.java index c19163af374..b04f51b323a 100644 --- a/src/main/java/org/traccar/model/LogRecord.java +++ b/src/main/java/org/traccar/model/LogRecord.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Anton Tananaev (anton@traccar.org) + * Copyright 2023 - 2024 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. @@ -16,24 +16,33 @@ package org.traccar.model; import com.fasterxml.jackson.annotation.JsonIgnore; +import org.traccar.session.ConnectionKey; import java.net.InetSocketAddress; +import java.net.SocketAddress; public class LogRecord { - private InetSocketAddress address; + private final InetSocketAddress localAddress; + private final InetSocketAddress remoteAddress; - public void setAddress(InetSocketAddress address) { - this.address = address; + public LogRecord(SocketAddress localAddress, SocketAddress remoteAddress) { + this.localAddress = (InetSocketAddress) localAddress; + this.remoteAddress = (InetSocketAddress) remoteAddress; } @JsonIgnore public InetSocketAddress getAddress() { - return address; + return remoteAddress; + } + + @JsonIgnore + public ConnectionKey getConnectionKey() { + return new ConnectionKey(localAddress, remoteAddress); } public String getHost() { - return address.getHostString(); + return remoteAddress.getHostString(); } private String protocol; diff --git a/src/main/java/org/traccar/session/ConnectionKey.java b/src/main/java/org/traccar/session/ConnectionKey.java new file mode 100644 index 00000000000..3b7e2ebf876 --- /dev/null +++ b/src/main/java/org/traccar/session/ConnectionKey.java @@ -0,0 +1,54 @@ +/* + * Copyright 2024 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.session; + +import io.netty.channel.Channel; + +import java.net.SocketAddress; +import java.util.Objects; + +public class ConnectionKey { + + private final SocketAddress localAddress; + private final SocketAddress remoteAddress; + + public ConnectionKey(Channel channel, SocketAddress remoteAddress) { + this(channel.localAddress(), remoteAddress); + } + + public ConnectionKey(SocketAddress localAddress, SocketAddress remoteAddress) { + this.localAddress = localAddress; + this.remoteAddress = remoteAddress; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ConnectionKey that = (ConnectionKey) o; + return Objects.equals(localAddress, that.localAddress) && Objects.equals(remoteAddress, that.remoteAddress); + } + + @Override + public int hashCode() { + return Objects.hash(localAddress, remoteAddress); + } + +} diff --git a/src/main/java/org/traccar/session/ConnectionManager.java b/src/main/java/org/traccar/session/ConnectionManager.java index 42dcf5ce97d..8431a0327e1 100644 --- a/src/main/java/org/traccar/session/ConnectionManager.java +++ b/src/main/java/org/traccar/session/ConnectionManager.java @@ -66,8 +66,8 @@ public class ConnectionManager implements BroadcastInterface { private final boolean showUnknownDevices; private final Map sessionsByDeviceId = new ConcurrentHashMap<>(); - private final Map> sessionsByEndpoint = new ConcurrentHashMap<>(); - private final Map unknownByEndpoint = new ConcurrentHashMap<>(); + private final Map> sessionsByEndpoint = new ConcurrentHashMap<>(); + private final Map unknownByEndpoint = new ConcurrentHashMap<>(); private final Config config; private final CacheManager cacheManager; @@ -108,8 +108,9 @@ public DeviceSession getDeviceSession( Protocol protocol, Channel channel, SocketAddress remoteAddress, String... uniqueIds) throws Exception { + ConnectionKey connectionKey = new ConnectionKey(channel, remoteAddress); Map endpointSessions = sessionsByEndpoint.getOrDefault( - remoteAddress, new ConcurrentHashMap<>()); + connectionKey, new ConcurrentHashMap<>()); uniqueIds = Arrays.stream(uniqueIds).filter(Objects::nonNull).toArray(String[]::new); if (uniqueIds.length > 0) { @@ -133,23 +134,23 @@ public DeviceSession getDeviceSession( } if (device != null) { - unknownByEndpoint.remove(remoteAddress); + unknownByEndpoint.remove(connectionKey); device.checkDisabled(); DeviceSession oldSession = sessionsByDeviceId.remove(device.getId()); if (oldSession != null) { - Map oldEndpointSessions = sessionsByEndpoint.get(oldSession.getRemoteAddress()); + Map oldEndpointSessions = sessionsByEndpoint.get(oldSession.getConnectionKey()); if (oldEndpointSessions != null && oldEndpointSessions.size() > 1) { oldEndpointSessions.remove(device.getUniqueId()); } else { - sessionsByEndpoint.remove(oldSession.getRemoteAddress()); + sessionsByEndpoint.remove(oldSession.getConnectionKey()); } } DeviceSession deviceSession = new DeviceSession( device.getId(), device.getUniqueId(), device.getModel(), protocol, channel, remoteAddress); endpointSessions.put(device.getUniqueId(), deviceSession); - sessionsByEndpoint.put(remoteAddress, endpointSessions); + sessionsByEndpoint.put(connectionKey, endpointSessions); sessionsByDeviceId.put(device.getId(), deviceSession); if (oldSession == null) { @@ -158,7 +159,7 @@ public DeviceSession getDeviceSession( return deviceSession; } else { - unknownByEndpoint.put(remoteAddress, firstUniqueId); + unknownByEndpoint.put(connectionKey, firstUniqueId); LOGGER.warn("Unknown device - " + String.join(" ", uniqueIds) + " (" + ((InetSocketAddress) remoteAddress).getHostString() + ")"); return null; @@ -189,7 +190,8 @@ private Device addUnknownDevice(String uniqueId) { public void deviceDisconnected(Channel channel, boolean supportsOffline) { SocketAddress remoteAddress = channel.remoteAddress(); if (remoteAddress != null) { - Map endpointSessions = sessionsByEndpoint.remove(remoteAddress); + ConnectionKey connectionKey = new ConnectionKey(channel, remoteAddress); + Map endpointSessions = sessionsByEndpoint.remove(connectionKey); if (endpointSessions != null) { for (DeviceSession deviceSession : endpointSessions.values()) { if (supportsOffline) { @@ -199,7 +201,7 @@ public void deviceDisconnected(Channel channel, boolean supportsOffline) { cacheManager.removeDevice(deviceSession.getDeviceId()); } } - unknownByEndpoint.remove(remoteAddress); + unknownByEndpoint.remove(connectionKey); } } @@ -212,7 +214,7 @@ private void removeDeviceSession(long deviceId) { DeviceSession deviceSession = sessionsByDeviceId.remove(deviceId); if (deviceSession != null) { cacheManager.removeDevice(deviceId); - sessionsByEndpoint.computeIfPresent(deviceSession.getRemoteAddress(), (e, sessions) -> { + sessionsByEndpoint.computeIfPresent(deviceSession.getConnectionKey(), (e, sessions) -> { sessions.remove(deviceSession.getUniqueId()); return sessions.isEmpty() ? null : sessions; }); @@ -345,9 +347,9 @@ public synchronized void invalidate } public synchronized void updateLog(LogRecord record) { - var sessions = sessionsByEndpoint.getOrDefault(record.getAddress(), Map.of()); + var sessions = sessionsByEndpoint.getOrDefault(record.getConnectionKey(), Map.of()); if (sessions.isEmpty()) { - String unknownUniqueId = unknownByEndpoint.get(record.getAddress()); + String unknownUniqueId = unknownByEndpoint.get(record.getConnectionKey()); if (unknownUniqueId != null && showUnknownDevices) { record.setUniqueId(unknownUniqueId); listeners.values().stream() diff --git a/src/main/java/org/traccar/session/DeviceSession.java b/src/main/java/org/traccar/session/DeviceSession.java index f124ca7f9f0..31d5e6a1380 100644 --- a/src/main/java/org/traccar/session/DeviceSession.java +++ b/src/main/java/org/traccar/session/DeviceSession.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2024 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. @@ -61,8 +61,8 @@ public Channel getChannel() { return channel; } - public SocketAddress getRemoteAddress() { - return remoteAddress; + public ConnectionKey getConnectionKey() { + return new ConnectionKey(channel, remoteAddress); } public boolean supportsLiveCommands() { From b6be94fef718926f5575257ab2724f5e538e215f Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Wed, 20 Mar 2024 20:32:16 -0700 Subject: [PATCH 15/42] Support 4G TK905B-G battery level --- src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java | 3 ++- .../java/org/traccar/protocol/HuabaoProtocolDecoderTest.java | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java index 0a8540543b1..2186fb91ff0 100644 --- a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java @@ -678,7 +678,8 @@ private Position decodeLocation(DeviceSession deviceSession, ByteBuf buf) { buf.readUnsignedShort(), buf.readUnsignedInt())); position.setNetwork(network); break; - case 0xE1: + case 0x00A8: + case 0x00E1: position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte()); break; default: diff --git a/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java index 164635109d0..7fd7cc59927 100644 --- a/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java @@ -11,6 +11,10 @@ public void testDecode() throws Exception { var decoder = inject(new HuabaoProtocolDecoder(null)); + verifyAttribute(decoder, binary( + "7E0200005300959000194400080000000000000003015DA64806CCB4A8001100000000230816014137010400005B3F30011F310112EB29000C00B28986049910219020787400060089FFFFFFFF000600C5FFFFFFEF0004002D0F4E000300A84CE07E"), + Position.KEY_BATTERY_LEVEL, 76); + verifyAttribute(decoder, binary( "7e55018c418560090010174701022106242122348476113550490700001c06000000074e0000000000000308100000100102020200000a030000000b0803363839363037650c0600000001000afa7e"), "unlockResult", 0x65); From 13f6415ba0a8ca76d1a2cbb3d0ea199361295f1b Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Thu, 28 Mar 2024 17:58:38 -0700 Subject: [PATCH 16/42] Minor formatting change --- .../java/org/traccar/protocol/AutoTrackProtocolDecoder.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/traccar/protocol/AutoTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/AutoTrackProtocolDecoder.java index c072e55d0a6..938d170e622 100644 --- a/src/main/java/org/traccar/protocol/AutoTrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/AutoTrackProtocolDecoder.java @@ -101,10 +101,11 @@ protected Object decode( int type = buf.readUnsignedByte(); buf.readUnsignedShortLE(); // length + DeviceSession deviceSession; switch (type) { case MSG_LOGIN_REQUEST: String imei = ByteBufUtil.hexDump(buf.readSlice(8)); - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei); + deviceSession = getDeviceSession(channel, remoteAddress, imei); if (deviceSession == null) { return null; } From 0bf22eb231e3eb9ecf912f41dcc8f5e7f7fa8865 Mon Sep 17 00:00:00 2001 From: jcardus Date: Fri, 29 Mar 2024 01:10:06 +0000 Subject: [PATCH 17/42] Fix INF message on GV310LAU --- .../protocol/Gl200TextProtocolDecoder.java | 28 +++++++++++++++---- .../Gl200TextProtocolDecoderTest.java | 11 ++++++++ 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java index 7a8d67cf920..b998f718e63 100644 --- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java @@ -122,12 +122,12 @@ private Position decodeAck(Channel channel, SocketAddress remoteAddress, String[ private static final Pattern PATTERN_INF = new PatternBuilder() .text("+").expression("(?:RESP|BUFF):GTINF,") - .expression("(?:.{6}|.{10})?,") // protocol version + .expression("(.{6}|.{10})?,") // protocol version .number("(d{15}|x{14}),") // imei .expression("(?:[0-9A-Z]{17},)?") // vin .expression("(?:[^,]+)?,") // device name .number("(xx),") // state - .expression("(?:[0-9Ff]{20})?,") // iccid + .expression("([0-9Ff]{20})?,") // iccid .number("(d{1,2}),") // rssi .number("d{1,2},") .expression("[01]{1,2},") // external power @@ -148,6 +148,7 @@ private Position decodeAck(Channel channel, SocketAddress remoteAddress, String[ .expression("(?:[01])?,").optional() // pin15 mode .number("(d+)?,") // adc1 .number("(d+)?,").optional() // adc2 + .number("(d+)?,").optional() // adc3 .number("(xx)?,") // digital input .number("(xx)?,") // digital output .number("[-+]dddd,") // timezone @@ -163,10 +164,17 @@ private Position decodeAck(Channel channel, SocketAddress remoteAddress, String[ private Object decodeInf(Channel channel, SocketAddress remoteAddress, String sentence) { Parser parser = new Parser(PATTERN_INF, sentence); - Position position = initPosition(parser, channel, remoteAddress); - if (position == null) { + if (!parser.matches()) { return null; } + String protocolVersion = parser.next(); + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + Position position = new Position(); + position.setProtocol(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); switch (parser.nextHexInt()) { case 0x16: @@ -197,9 +205,14 @@ private Object decodeInf(Channel channel, SocketAddress remoteAddress, String se break; } + position.set(Position.KEY_ICCID, parser.next()); position.set(Position.KEY_RSSI, parser.nextInt()); - - parser.next(); // odometer or external power + + if (protocolVersion.startsWith("6E")) { // GV310LAU + position.set(Position.KEY_POWER, parser.nextDouble() / 1000); // odometer or external power + } else { + parser.next(); // odometer or external power + } position.set(Position.KEY_BATTERY, parser.nextDouble()); position.set(Position.KEY_CHARGE, parser.nextInt() == 1); @@ -210,6 +223,9 @@ private Object decodeInf(Channel channel, SocketAddress remoteAddress, String se position.set(Position.PREFIX_ADC + 1, parser.next()); position.set(Position.PREFIX_ADC + 2, parser.next()); + if (protocolVersion.startsWith("6E")) { + position.set(Position.PREFIX_ADC + 3, parser.next()); + } position.set(Position.KEY_INPUT, parser.next()); position.set(Position.KEY_OUTPUT, parser.next()); diff --git a/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java index b6e50e07d54..768704b68fc 100644 --- a/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java @@ -487,6 +487,17 @@ public void testDecode() throws Exception { verifyAttributes(decoder, buffer( "+ACK:GTGEO,1A0102,135790246811220,,0,0008,20100310172830,11F0")); + verifyAttributes(decoder, buffer( + "+RESP:GTINF,6E0202,868589060187625,RA82,11,89883030000091225018,41,0,1,12349,,4.15,0,1,0,0,20240328231013,0,0,0,0,00,00,+0000,0,20240328231015,7D4F")); + + verifyAttribute(decoder, buffer( + "+RESP:GTINF,6E0202,868589060187625,RA82,11,89883030000091225018,41,0,1,12349,,4.15,0,1,0,0,20240328231013,0,0,0,0,00,00,+0000,0,20240328231015,7D4F"), + Position.KEY_POWER, 12.349); + + verifyAttribute(decoder, buffer( + "+RESP:GTINF,6E0202,868589060187625,RA82,11,89883030000091225018,41,0,1,12349,,4.15,0,1,0,0,20240328231013,0,0,0,0,00,00,+0000,0,20240328231015,7D4F"), + Position.PREFIX_ADC + 3, "0"); + decoder.setModelOverride("GV355CEU"); verifyAttributes(decoder, buffer( From 2a57c25829fc11c2fcf4d55aa6b866f8fc6305f7 Mon Sep 17 00:00:00 2001 From: jcardus Date: Fri, 29 Mar 2024 01:17:59 +0000 Subject: [PATCH 18/42] fix trailing spaces --- .../java/org/traccar/protocol/Gl200TextProtocolDecoder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java index b998f718e63..c0a2a2ea048 100644 --- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java @@ -207,7 +207,7 @@ private Object decodeInf(Channel channel, SocketAddress remoteAddress, String se position.set(Position.KEY_ICCID, parser.next()); position.set(Position.KEY_RSSI, parser.nextInt()); - + if (protocolVersion.startsWith("6E")) { // GV310LAU position.set(Position.KEY_POWER, parser.nextDouble() / 1000); // odometer or external power } else { From 97b61ee431d8384ad2c233adcd9e478b074bc41d Mon Sep 17 00:00:00 2001 From: jcardus Date: Fri, 29 Mar 2024 19:15:38 +0000 Subject: [PATCH 19/42] start refactoring --- .../protocol/Gl200TextProtocolDecoder.java | 52 ++++++++++++++++--- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java index c0a2a2ea048..0e08066ab9b 100644 --- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java @@ -42,6 +42,7 @@ import java.util.LinkedList; import java.util.List; import java.util.TimeZone; +import java.util.HashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -51,6 +52,37 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { private final DateFormat dateFormat; + private static final HashMap devices = new HashMap() {{ + put("52", "GL50"); + put("55", "GL50B"); + put("02", "GL200"); + put("04", "GV200"); + put("06", "GV300"); + put("08", "GMT100"); + put("09", "GV50P"); // GV50 Plus + put("0F", "GV55"); + put("50", "GV55W"); + put("10", "GV55 LITE"); + put("11", "GL500"); + put("1A", "GL300"); + put("1F", "GV500"); + put("5E", "GV500MAP"); + put("25", "GV300"); // New Version + put("35", "GV200"); // New Version + put("27", "GV300W"); + put("2F", "GV55"); // New Version + put("30", "GL300"); // New Version + put("36", "GV500"); // New Version + put("2C", "GL300W"); // New version + put("3F", "GMT100"); // New version + put("F8", "GV800W"); + put("41", "GV75W"); + put("FC", "GV600W"); + put("6E", "GV310LAU"); + put("802004", "GV58LAU"); + put("802005", "GV355CEU"); + }}; + public Gl200TextProtocolDecoder(Protocol protocol) { super(protocol); dateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); @@ -62,9 +94,14 @@ protected void init() { ignoreFixTime = getConfig().getBoolean(Keys.PROTOCOL_IGNORE_FIX_TIME.withPrefix(getProtocolName())); } - private String getDeviceModel(DeviceSession deviceSession, String value) { - String model = value.isEmpty() ? getDeviceModel(deviceSession) : value; - return model != null ? model.toUpperCase() : ""; + private String getDeviceModel(String protocolVersion) { + if (devices.containsKey(protocolVersion.substring(0, 2))) { + return devices.get(protocolVersion.substring(0, 2)); + } + if (devices.containsKey(protocolVersion.substring(0, 6))) { + return devices.get(protocolVersion.substring(0, 6)); + } + return ""; } private Position initPosition(Parser parser, Channel channel, SocketAddress remoteAddress) { @@ -471,7 +508,7 @@ private Object decodeObd(Channel channel, SocketAddress remoteAddress, String se private Object decodeCan(Channel channel, SocketAddress remoteAddress, String[] v) throws ParseException { int index = 0; index += 1; // header - index += 1; // protocol version + String protocolVersion = v[index++]; // protocol version DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, v[index++]); if (deviceSession == null) { return null; @@ -480,7 +517,8 @@ private Object decodeCan(Channel channel, SocketAddress remoteAddress, String[] Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); - String model = getDeviceModel(deviceSession, v[index++]); + String model = getDeviceModel(protocolVersion); + index += 1; // device name index += 1; // report type index += 1; // can bus state long reportMask = Long.parseLong(v[index++], 16); @@ -888,13 +926,13 @@ private Object decodeFri(Channel channel, SocketAddress remoteAddress, String se private Object decodeEri(Channel channel, SocketAddress remoteAddress, String[] v) throws ParseException { int index = 0; index += 1; // header - index += 1; // protocol version + String protocolVersion = v[index++]; // protocol version DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, v[index++]); if (deviceSession == null) { return null; } - String model = getDeviceModel(deviceSession, v[index++]); + String model = getDeviceModel(protocolVersion); long mask = Long.parseLong(v[index++], 16); Double power = v[index++].isEmpty() ? null : Integer.parseInt(v[index - 1]) * 0.001; index += 1; // report type From b95fa6a433fe9fc464aa59256d0b33da46809d0b Mon Sep 17 00:00:00 2001 From: jcardus Date: Fri, 29 Mar 2024 19:43:06 +0000 Subject: [PATCH 20/42] C2 and F1 fallback to device name and attribute --- .../traccar/protocol/Gl200TextProtocolDecoder.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java index 0e08066ab9b..817e03f6771 100644 --- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java @@ -79,6 +79,8 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { put("41", "GV75W"); put("FC", "GV600W"); put("6E", "GV310LAU"); + put("C2", "GV600M"); + put("F1", "GV350M"); put("802004", "GV58LAU"); put("802005", "GV355CEU"); }}; @@ -94,14 +96,15 @@ protected void init() { ignoreFixTime = getConfig().getBoolean(Keys.PROTOCOL_IGNORE_FIX_TIME.withPrefix(getProtocolName())); } - private String getDeviceModel(String protocolVersion) { + private String getDeviceModel(DeviceSession deviceSession, String value, String protocolVersion) { if (devices.containsKey(protocolVersion.substring(0, 2))) { return devices.get(protocolVersion.substring(0, 2)); } if (devices.containsKey(protocolVersion.substring(0, 6))) { return devices.get(protocolVersion.substring(0, 6)); } - return ""; + String model = value.isEmpty() ? getDeviceModel(deviceSession) : value; + return model != null ? model.toUpperCase() : ""; } private Position initPosition(Parser parser, Channel channel, SocketAddress remoteAddress) { @@ -517,8 +520,7 @@ private Object decodeCan(Channel channel, SocketAddress remoteAddress, String[] Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); - String model = getDeviceModel(protocolVersion); - index += 1; // device name + String model = getDeviceModel(deviceSession, v[index++], protocolVersion); index += 1; // report type index += 1; // can bus state long reportMask = Long.parseLong(v[index++], 16); @@ -932,7 +934,7 @@ private Object decodeEri(Channel channel, SocketAddress remoteAddress, String[] return null; } - String model = getDeviceModel(protocolVersion); + String model = getDeviceModel(deviceSession, v[index++], protocolVersion); long mask = Long.parseLong(v[index++], 16); Double power = v[index++].isEmpty() ? null : Integer.parseInt(v[index - 1]) * 0.001; index += 1; // report type From 7ed2a8b54d9e269b0b5d180dc976d38926cd5bac Mon Sep 17 00:00:00 2001 From: jcardus Date: Fri, 29 Mar 2024 19:58:44 +0000 Subject: [PATCH 21/42] order device models by id use getDeviceModel --- .../protocol/Gl200TextProtocolDecoder.java | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java index 817e03f6771..16098bc0557 100644 --- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java @@ -52,35 +52,35 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { private final DateFormat dateFormat; - private static final HashMap devices = new HashMap() {{ - put("52", "GL50"); - put("55", "GL50B"); + private static final HashMap devices = new HashMap() {{ put("02", "GL200"); put("04", "GV200"); put("06", "GV300"); put("08", "GMT100"); put("09", "GV50P"); // GV50 Plus put("0F", "GV55"); - put("50", "GV55W"); put("10", "GV55 LITE"); put("11", "GL500"); put("1A", "GL300"); put("1F", "GV500"); - put("5E", "GV500MAP"); put("25", "GV300"); // New Version - put("35", "GV200"); // New Version put("27", "GV300W"); + put("2C", "GL300W"); // New version put("2F", "GV55"); // New Version put("30", "GL300"); // New Version + put("35", "GV200"); // New Version put("36", "GV500"); // New Version - put("2C", "GL300W"); // New version put("3F", "GMT100"); // New version - put("F8", "GV800W"); put("41", "GV75W"); - put("FC", "GV600W"); + put("50", "GV55W"); + put("52", "GL50"); + put("55", "GL50B"); + put("5E", "GV500MAP"); put("6E", "GV310LAU"); put("C2", "GV600M"); put("F1", "GV350M"); + put("F8", "GV800W"); + put("FC", "GV600W"); put("802004", "GV58LAU"); put("802005", "GV355CEU"); }}; @@ -248,7 +248,8 @@ private Object decodeInf(Channel channel, SocketAddress remoteAddress, String se position.set(Position.KEY_ICCID, parser.next()); position.set(Position.KEY_RSSI, parser.nextInt()); - if (protocolVersion.startsWith("6E")) { // GV310LAU + String model = getDeviceModel(deviceSession, "", protocolVersion); + if (model.equals("GV310LAU")) { position.set(Position.KEY_POWER, parser.nextDouble() / 1000); // odometer or external power } else { parser.next(); // odometer or external power @@ -263,7 +264,7 @@ private Object decodeInf(Channel channel, SocketAddress remoteAddress, String se position.set(Position.PREFIX_ADC + 1, parser.next()); position.set(Position.PREFIX_ADC + 2, parser.next()); - if (protocolVersion.startsWith("6E")) { + if (model.equals("GV310LAU")) { position.set(Position.PREFIX_ADC + 3, parser.next()); } From 1450fc6a6cb3d56eefff7d0f222a868b3568c592 Mon Sep 17 00:00:00 2001 From: jcardus Date: Fri, 29 Mar 2024 20:04:18 +0000 Subject: [PATCH 22/42] fix trailing spaces fix naming convention --- .../org/traccar/protocol/Gl200TextProtocolDecoder.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java index 16098bc0557..9b7737b36ee 100644 --- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java @@ -52,7 +52,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { private final DateFormat dateFormat; - private static final HashMap devices = new HashMap() {{ + private static final HashMap DEVICE_MODELS = new HashMap() {{ put("02", "GL200"); put("04", "GV200"); put("06", "GV300"); @@ -97,11 +97,11 @@ protected void init() { } private String getDeviceModel(DeviceSession deviceSession, String value, String protocolVersion) { - if (devices.containsKey(protocolVersion.substring(0, 2))) { - return devices.get(protocolVersion.substring(0, 2)); + if (DEVICE_MODELS.containsKey(protocolVersion.substring(0, 2))) { + return DEVICE_MODELS.get(protocolVersion.substring(0, 2)); } - if (devices.containsKey(protocolVersion.substring(0, 6))) { - return devices.get(protocolVersion.substring(0, 6)); + if (DEVICE_MODELS.containsKey(protocolVersion.substring(0, 6))) { + return DEVICE_MODELS.get(protocolVersion.substring(0, 6)); } String model = value.isEmpty() ? getDeviceModel(deviceSession) : value; return model != null ? model.toUpperCase() : ""; From 078ddb342c7af83d22767224a7d5b7c7e8ab4fe7 Mon Sep 17 00:00:00 2001 From: jcardus Date: Fri, 29 Mar 2024 21:57:07 +0000 Subject: [PATCH 23/42] Statics should be above instance variables. remove comments --- .../protocol/Gl200TextProtocolDecoder.java | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java index 9b7737b36ee..e30fafcaf8e 100644 --- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java @@ -47,30 +47,26 @@ import java.util.regex.Pattern; public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { - - private boolean ignoreFixTime; - - private final DateFormat dateFormat; - + private static final HashMap DEVICE_MODELS = new HashMap() {{ put("02", "GL200"); put("04", "GV200"); put("06", "GV300"); put("08", "GMT100"); - put("09", "GV50P"); // GV50 Plus + put("09", "GV50P"); put("0F", "GV55"); put("10", "GV55 LITE"); put("11", "GL500"); put("1A", "GL300"); put("1F", "GV500"); - put("25", "GV300"); // New Version + put("25", "GV300"); put("27", "GV300W"); - put("2C", "GL300W"); // New version - put("2F", "GV55"); // New Version - put("30", "GL300"); // New Version - put("35", "GV200"); // New Version - put("36", "GV500"); // New Version - put("3F", "GMT100"); // New version + put("2C", "GL300W"); + put("2F", "GV55"); + put("30", "GL300"); + put("35", "GV200"); + put("36", "GV500"); + put("3F", "GMT100"); put("41", "GV75W"); put("50", "GV55W"); put("52", "GL50"); @@ -85,6 +81,10 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { put("802005", "GV355CEU"); }}; + private boolean ignoreFixTime; + + private final DateFormat dateFormat; + public Gl200TextProtocolDecoder(Protocol protocol) { super(protocol); dateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); @@ -108,13 +108,13 @@ private String getDeviceModel(DeviceSession deviceSession, String value, String } private Position initPosition(Parser parser, Channel channel, SocketAddress remoteAddress) { - if (parser.matches()) { - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); - if (deviceSession != null) { - Position position = new Position(getProtocolName()); - position.setDeviceId(deviceSession.getDeviceId()); - return position; - } +if (parser.matches()) { + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession != null) { + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + return position; +} } return null; } @@ -294,7 +294,7 @@ private Object decodeInf(Channel channel, SocketAddress remoteAddress, String se private Object decodeVer(Channel channel, SocketAddress remoteAddress, String sentence) { Parser parser = new Parser(PATTERN_VER, sentence); - Position position = initPosition(parser, channel, remoteAddress); + Position position = initPosition(parser, channel, remoteAddress); if (position == null) { return null; } From b0b1257473b8895ba4e8b867442c13f5b7c048f7 Mon Sep 17 00:00:00 2001 From: jcardus Date: Fri, 29 Mar 2024 21:59:13 +0000 Subject: [PATCH 24/42] fix identation --- .../traccar/protocol/Gl200TextProtocolDecoder.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java index e30fafcaf8e..bf86440334b 100644 --- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java @@ -108,13 +108,13 @@ private String getDeviceModel(DeviceSession deviceSession, String value, String } private Position initPosition(Parser parser, Channel channel, SocketAddress remoteAddress) { -if (parser.matches()) { - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); - if (deviceSession != null) { - Position position = new Position(getProtocolName()); - position.setDeviceId(deviceSession.getDeviceId()); - return position; -} + if (parser.matches()) { + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession != null) { + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + return position; + } } return null; } From 51dd3b44a6e81f523f37aacb23ad5b39de1d9eca Mon Sep 17 00:00:00 2001 From: jcardus Date: Fri, 29 Mar 2024 22:00:32 +0000 Subject: [PATCH 25/42] remove trailing spaces --- .../java/org/traccar/protocol/Gl200TextProtocolDecoder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java index bf86440334b..b953aa8cac3 100644 --- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java @@ -83,7 +83,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { private boolean ignoreFixTime; - private final DateFormat dateFormat; + private final DateFormat dateFormat; public Gl200TextProtocolDecoder(Protocol protocol) { super(protocol); From 432a59c8e15e0ce5ed0cb472864f627e3eadea6e Mon Sep 17 00:00:00 2001 From: jcardus Date: Fri, 29 Mar 2024 22:26:21 +0000 Subject: [PATCH 26/42] 50: Line has trailing spaces --- .../java/org/traccar/protocol/Gl200TextProtocolDecoder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java index b953aa8cac3..1d4688e6941 100644 --- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java @@ -47,7 +47,7 @@ import java.util.regex.Pattern; public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { - + private static final HashMap DEVICE_MODELS = new HashMap() {{ put("02", "GL200"); put("04", "GV200"); From 829387a48e278ea7428ee719cf6973ecf36271b9 Mon Sep 17 00:00:00 2001 From: jcardus Date: Fri, 29 Mar 2024 22:32:00 +0000 Subject: [PATCH 27/42] weird --- .../java/org/traccar/protocol/Gl200TextProtocolDecoder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java index 1d4688e6941..62335210630 100644 --- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java @@ -294,7 +294,7 @@ private Object decodeInf(Channel channel, SocketAddress remoteAddress, String se private Object decodeVer(Channel channel, SocketAddress remoteAddress, String sentence) { Parser parser = new Parser(PATTERN_VER, sentence); - Position position = initPosition(parser, channel, remoteAddress); + Position position = initPosition(parser, channel, remoteAddress); if (position == null) { return null; } From 9e750264dd9a1cbfbd7e1aadfdeedbcdbb5d0a0d Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 30 Mar 2024 07:19:43 -0700 Subject: [PATCH 28/42] Refactor and add more models --- .../protocol/Gl200TextProtocolDecoder.java | 104 ++++++++++-------- .../Gl200TextProtocolDecoderTest.java | 16 ++- 2 files changed, 65 insertions(+), 55 deletions(-) diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java index 62335210630..5ac7b241716 100644 --- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java @@ -41,45 +41,52 @@ import java.util.Date; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.TimeZone; -import java.util.HashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { - private static final HashMap DEVICE_MODELS = new HashMap() {{ - put("02", "GL200"); - put("04", "GV200"); - put("06", "GV300"); - put("08", "GMT100"); - put("09", "GV50P"); - put("0F", "GV55"); - put("10", "GV55 LITE"); - put("11", "GL500"); - put("1A", "GL300"); - put("1F", "GV500"); - put("25", "GV300"); - put("27", "GV300W"); - put("2C", "GL300W"); - put("2F", "GV55"); - put("30", "GL300"); - put("35", "GV200"); - put("36", "GV500"); - put("3F", "GMT100"); - put("41", "GV75W"); - put("50", "GV55W"); - put("52", "GL50"); - put("55", "GL50B"); - put("5E", "GV500MAP"); - put("6E", "GV310LAU"); - put("C2", "GV600M"); - put("F1", "GV350M"); - put("F8", "GV800W"); - put("FC", "GV600W"); - put("802004", "GV58LAU"); - put("802005", "GV355CEU"); - }}; + private static final Map PROTOCOL_MODELS = Map.ofEntries( + Map.entry("02", "GL200"), + Map.entry("04", "GV200"), + Map.entry("06", "GV300"), + Map.entry("08", "GMT100"), + Map.entry("09", "GV50P"), + Map.entry("0F", "GV55"), + Map.entry("10", "GV55 LITE"), + Map.entry("11", "GL500"), + Map.entry("1A", "GL300"), + Map.entry("1F", "GV500"), + Map.entry("25", "GV300"), + Map.entry("27", "GV300W"), + Map.entry("28", "GL300VC"), + Map.entry("2C", "GL300W"), + Map.entry("2F", "GV55"), + Map.entry("30", "GL300"), + Map.entry("31", "GV65"), + Map.entry("35", "GV200"), + Map.entry("36", "GV500"), + Map.entry("3F", "GMT100"), + Map.entry("40", "GL500"), + Map.entry("41", "GV75W"), + Map.entry("42", "GT501"), + Map.entry("44", "GL530"), + Map.entry("45", "GB100"), + Map.entry("50", "GV55W"), + Map.entry("52", "GL50"), + Map.entry("55", "GL50B"), + Map.entry("5E", "GV500MAP"), + Map.entry("6E", "GV310LAU"), + Map.entry("C2", "GV600M"), + Map.entry("DC", "GV600MG"), + Map.entry("DE", "GL500M"), + Map.entry("F1", "GV350M"), + Map.entry("F8", "GV800W"), + Map.entry("FC", "GV600W"), + Map.entry("802004", "GV58LAU"), + Map.entry("802005", "GV355CEU")); private boolean ignoreFixTime; @@ -96,15 +103,18 @@ protected void init() { ignoreFixTime = getConfig().getBoolean(Keys.PROTOCOL_IGNORE_FIX_TIME.withPrefix(getProtocolName())); } - private String getDeviceModel(DeviceSession deviceSession, String value, String protocolVersion) { - if (DEVICE_MODELS.containsKey(protocolVersion.substring(0, 2))) { - return DEVICE_MODELS.get(protocolVersion.substring(0, 2)); + private String getDeviceModel(DeviceSession deviceSession, String protocolVersion) { + String declaredModel = getDeviceModel(deviceSession); + if (declaredModel != null) { + return declaredModel.toUpperCase(); } - if (DEVICE_MODELS.containsKey(protocolVersion.substring(0, 6))) { - return DEVICE_MODELS.get(protocolVersion.substring(0, 6)); + if (PROTOCOL_MODELS.containsKey(protocolVersion.substring(0, 2))) { + return PROTOCOL_MODELS.get(protocolVersion.substring(0, 2)); } - String model = value.isEmpty() ? getDeviceModel(deviceSession) : value; - return model != null ? model.toUpperCase() : ""; + if (PROTOCOL_MODELS.containsKey(protocolVersion.substring(0, 6))) { + return PROTOCOL_MODELS.get(protocolVersion.substring(0, 6)); + } + return ""; } private Position initPosition(Parser parser, Channel channel, SocketAddress remoteAddress) { @@ -162,12 +172,12 @@ private Position decodeAck(Channel channel, SocketAddress remoteAddress, String[ private static final Pattern PATTERN_INF = new PatternBuilder() .text("+").expression("(?:RESP|BUFF):GTINF,") - .expression("(.{6}|.{10})?,") // protocol version + .expression("(.{6}|.{10})?,") // protocol version .number("(d{15}|x{14}),") // imei .expression("(?:[0-9A-Z]{17},)?") // vin .expression("(?:[^,]+)?,") // device name .number("(xx),") // state - .expression("([0-9Ff]{20})?,") // iccid + .expression("([0-9Ff]{20})?,") // iccid .number("(d{1,2}),") // rssi .number("d{1,2},") .expression("[01]{1,2},") // external power @@ -248,9 +258,9 @@ private Object decodeInf(Channel channel, SocketAddress remoteAddress, String se position.set(Position.KEY_ICCID, parser.next()); position.set(Position.KEY_RSSI, parser.nextInt()); - String model = getDeviceModel(deviceSession, "", protocolVersion); + String model = getDeviceModel(deviceSession, protocolVersion); if (model.equals("GV310LAU")) { - position.set(Position.KEY_POWER, parser.nextDouble() / 1000); // odometer or external power + position.set(Position.KEY_POWER, parser.nextDouble() / 1000); } else { parser.next(); // odometer or external power } @@ -521,7 +531,8 @@ private Object decodeCan(Channel channel, SocketAddress remoteAddress, String[] Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); - String model = getDeviceModel(deviceSession, v[index++], protocolVersion); + String model = getDeviceModel(deviceSession, protocolVersion); + index += 1; // device name index += 1; // report type index += 1; // can bus state long reportMask = Long.parseLong(v[index++], 16); @@ -935,7 +946,8 @@ private Object decodeEri(Channel channel, SocketAddress remoteAddress, String[] return null; } - String model = getDeviceModel(deviceSession, v[index++], protocolVersion); + String model = getDeviceModel(deviceSession, protocolVersion); + index += 1; // device name long mask = Long.parseLong(v[index++], 16); Double power = v[index++].isEmpty() ? null : Integer.parseInt(v[index - 1]) * 0.001; index += 1; // report type diff --git a/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java index 768704b68fc..bc1bee229bc 100644 --- a/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java @@ -52,14 +52,14 @@ public void testDecode() throws Exception { "+RESP:GTFRI,5E0100,862061048023666,,,12940,10,1,1,0.0,97,179.8,-90.366478,38.735379,20230616183231,0310,0410,6709,03ADF710,00,6223.7,,,,,110000,,,,202306161834$")); verifyAttribute(decoder, buffer( - "+BUFF:GTERI,410502,864802030794634,GV75W,00000001,,10,1,1,0.0,0,3027.8,-78.706612,-0.955699,20230418170736,0740,0002,A08C,2AB72D,00,0.0,,,,100,110000,1,0099,20230418171004,8B98$"), + "+BUFF:GTERI,410502,864802030794634,,00000001,,10,1,1,0.0,0,3027.8,-78.706612,-0.955699,20230418170736,0740,0002,A08C,2AB72D,00,0.0,,,,100,110000,1,0099,20230418171004,8B98$"), Position.KEY_FUEL_LEVEL, 153); verifyPositions(decoder, false, buffer( "+BUFF:GTFRI,2E0503,861106050005423,,,0,1,,,,,,,,,,,,0,0,,98,1,0,,,20200101000001,0083$")); verifyAttribute(decoder, buffer( - "+RESP:GTERI,271002,863457051562823,GV300,00000002,,10,1,1,0.0,15,28.2,-58.695253,-34.625413,20230119193305,0722,0007,1168,16B3BB,00,0.0,,,,99,210100,2,1,28F8A149F69A3C25,1,0190,20230119193314,07C7$"), + "+RESP:GTERI,271002,863457051562823,,00000002,,10,1,1,0.0,15,28.2,-58.695253,-34.625413,20230119193305,0722,0007,1168,16B3BB,00,0.0,,,,99,210100,2,1,28F8A149F69A3C25,1,0190,20230119193314,07C7$"), Position.PREFIX_TEMP + 1, 25.0); verifyAttribute(decoder, buffer( @@ -176,7 +176,7 @@ public void testDecode() throws Exception { "+RESP:GTERI,310603,863286023345490,,00000002,,10,1,2,0.3,0,155.7,8.000000,52.000000,20171215213040,0262,0002,1450,9F13,00,1130.3,00539:27:19,,,110000,2,1,28FFD5239115034E,1,,20171215213041,27C7$")); verifyPositions(decoder, buffer( - "+RESP:GTERI,250C02,868789023691057,GV300,00000019,,10,1,1,0.0,196,2258.0,-99.201807,19.559242,20180214002957,0334,0003,235B,7F8D,00,6786.7,,,,100,110000,1,0394,1,4,100.0,100.0,20180214003006,C72B$")); + "+RESP:GTERI,250C02,868789023691057,,00000019,,10,1,1,0.0,196,2258.0,-99.201807,19.559242,20180214002957,0334,0003,235B,7F8D,00,6786.7,,,,100,110000,1,0394,1,4,100.0,100.0,20180214003006,C72B$")); verifyAttributes(decoder, buffer( "+RESP:GTCAN,310603,863286023335723,gv65,00,1,C03FFFFF,,0,,719601.00,,,,,,,,274.99,179.02,95.98,84761.00,,,0,,0,,,0,0.0,216,29.8,-2.155296,51.899400,20180209172714,0234,0010,53F3,8D38,00,20180211002128,E94E$")); @@ -203,7 +203,7 @@ public void testDecode() throws Exception { "+RESP:GTOBD,360701,864251020253807,LSGTC58UX7Y067312,GV500,0,70FFFF,LSGTC58UX7Y067312,1,12309,983A8140,0,0,33,nan,,0,0,0,,10,0,,0,4.4,0,83.7,36.235142,49.967324,20170829112348,0255,0001,2760,9017,00,690.1,20170829112400,3456$")); verifyPositions(decoder, buffer( - "+RESP:GTERI,060502,861074023620928,GV300,00000002,27822,10,1,1,0.0,84,2870.9,-78.531796,-0.277329,20170825045344,,,,,,0.0,01138:30:24,,,83,220104,2,1,28FF2776A2150308,1,FFAD,0,20170825045348,A88C$")); + "+RESP:GTERI,060502,861074023620928,,00000002,27822,10,1,1,0.0,84,2870.9,-78.531796,-0.277329,20170825045344,,,,,,0.0,01138:30:24,,,83,220104,2,1,28FF2776A2150308,1,FFAD,0,20170825045348,A88C$")); verifyAttributes(decoder, buffer( "+RESP:GTINF,280500,A1000043D20139,GL300VC,41,,31,0,0,,,3.87,0,1,1,,,20170802150751,70,,48.0,,,20170802112145,03AC$")); @@ -218,7 +218,7 @@ public void testDecode() throws Exception { "+RESP:GTTRI,862370030005908,1,0,99,1,0.0,354,18.5,18.821100,-34.084002,20170607152024,0655,0001,00DD,1CAE,00,0103010100,20170607172115,3E7D$")); verifyPositions(decoder, buffer( - "+RESP:GTERI,060800,861074023677175,GV300,00000002,12351,10,1,1,0.0,0,2862.4,-78.467273,-0.164998,20170529181717,,,,,,0.0,00259:11:50,,,0,210104,2,1,28E17436060000E2,1,015F,0,20170529181723,2824$")); + "+RESP:GTERI,060800,861074023677175,,00000002,12351,10,1,1,0.0,0,2862.4,-78.467273,-0.164998,20170529181717,,,,,,0.0,00259:11:50,,,0,210104,2,1,28E17436060000E2,1,015F,0,20170529181723,2824$")); verifyPosition(decoder, buffer( "+RESP:GTSWG,110100,358688000000158,,1,0,2.1,0,27.1,121.390717,31.164424,20110901073917,0460,0000,1878,0873,,20110901154653,0015$")); @@ -239,7 +239,7 @@ public void testDecode() throws Exception { "+RESP:GTFRI,060228,862894020180553,,14827,10,1,1,3.4,199,409.6,-63.174466,-17.739317,20170407121823,0000,0000,0000,0000,00,15989.5,01070:43:13,13,180,0,220101,,,,20170407081824,9607$")); verifyPositions(decoder, buffer( - "+RESP:GTERI,060502,861074023376992,GV300,00000002,27239,10,1,1,0.2,312,183.3,-79.320820,-2.499110,20170401212005,0740,0000,EE4E,C98F,00,0.0,02114:36:35,,,90,220504,2,0,0,20170401212007,9E3D$")); + "+RESP:GTERI,060502,861074023376992,,00000002,27239,10,1,1,0.2,312,183.3,-79.320820,-2.499110,20170401212005,0740,0000,EE4E,C98F,00,0.0,02114:36:35,,,90,220504,2,0,0,20170401212007,9E3D$")); verifyPositions(decoder, buffer( "+RESP:GTFRI,060502,861074023689626,,25202,10,1,1,0.0,0,2744.1,-78.261047,0.023452,20170401211940,,,,,,0.0,00079:19:15,,,51,110000,,,,20170401212003,4DA7$")); @@ -251,7 +251,7 @@ public void testDecode() throws Exception { "+RESP:GTERI,06020B,862170010196747,,00000000,,10,1,2,1.8,0,-2.5,117.198440,31.845219,20120802061037,0460,0000,5663,0358,00,0.0,,,,0,410000,20120802061040,0012$")); verifyPositions(decoder, buffer( - "+RESP:GTERI,060502,861074023692562,GV300,00000002,14197,10,1,1,0.2,220,491.8,-79.064212,-2.159754,20170401212007,0740,0000,EE49,CE25,00,0.0,01509:10:58,,,87,220104,2,0,0,20170401212010,D14D$")); + "+RESP:GTERI,060502,861074023692562,,00000002,14197,10,1,1,0.2,220,491.8,-79.064212,-2.159754,20170401212007,0740,0000,EE49,CE25,00,0.0,01509:10:58,,,87,220104,2,0,0,20170401212010,D14D$")); verifyPositions(decoder, buffer( "+RESP:GTFRI,210102,354524044925825,,1,1,1,29,2.8,0,133.7,-90.203063,32.265473,20170318005208,,,,,10800,4,20170318005208,0002$")); @@ -498,8 +498,6 @@ public void testDecode() throws Exception { "+RESP:GTINF,6E0202,868589060187625,RA82,11,89883030000091225018,41,0,1,12349,,4.15,0,1,0,0,20240328231013,0,0,0,0,00,00,+0000,0,20240328231015,7D4F"), Position.PREFIX_ADC + 3, "0"); - decoder.setModelOverride("GV355CEU"); - verifyAttributes(decoder, buffer( "+RESP:GTCAN,8020050605,867488060270575,,00,1,FFFFFFFF,8LBETF3W4N0001613,,,22.54,0,,,,,,,7.84,4.61,3.24,3.33,,8080,,,00,0.00,0.00,1,14,14,2371,0,001FFFFF,,,,,,,,,7158,9998,0,7.84,0.00,0.00,558,,,,,,,C0,,,,,0,0.0,346,2848.5,-78.592371,-0.968132,20240202083437,0740,0002,526C,00AE7907,00,20240202083440,3F6D$")); From 3066f3ccfa2a674fae27922f9f94b5e16eb68f9b Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 30 Mar 2024 07:29:04 -0700 Subject: [PATCH 29/42] Model detection optimization --- .../traccar/protocol/Gl200TextProtocolDecoder.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java index 5ac7b241716..b14f64f4ac8 100644 --- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java @@ -79,6 +79,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { Map.entry("55", "GL50B"), Map.entry("5E", "GV500MAP"), Map.entry("6E", "GV310LAU"), + Map.entry("BD", "CV200"), Map.entry("C2", "GV600M"), Map.entry("DC", "GV600MG"), Map.entry("DE", "GL500M"), @@ -108,13 +109,14 @@ private String getDeviceModel(DeviceSession deviceSession, String protocolVersio if (declaredModel != null) { return declaredModel.toUpperCase(); } - if (PROTOCOL_MODELS.containsKey(protocolVersion.substring(0, 2))) { - return PROTOCOL_MODELS.get(protocolVersion.substring(0, 2)); - } - if (PROTOCOL_MODELS.containsKey(protocolVersion.substring(0, 6))) { - return PROTOCOL_MODELS.get(protocolVersion.substring(0, 6)); + String versionPrefix; + if (protocolVersion.length() > 6) { + versionPrefix = protocolVersion.substring(0, 6); + } else { + versionPrefix = protocolVersion.substring(0, 2); } - return ""; + String model = PROTOCOL_MODELS.get(versionPrefix); + return model != null ? model : ""; } private Position initPosition(Parser parser, Channel channel, SocketAddress remoteAddress) { From 8fe2d697064c88f5dadf5ca3aeca51ba7e925fd9 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 30 Mar 2024 07:38:13 -0700 Subject: [PATCH 30/42] Support virtual ignition messages --- .../org/traccar/protocol/Gl200TextProtocolDecoder.java | 6 ++++-- .../traccar/protocol/Gl200TextProtocolDecoderTest.java | 8 ++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java index b14f64f4ac8..775e9840187 100644 --- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java @@ -1031,7 +1031,7 @@ private Object decodeEri(Channel channel, SocketAddress remoteAddress, String[] } private static final Pattern PATTERN_IGN = new PatternBuilder() - .text("+").expression("(?:RESP|BUFF):GTIG[NF],") + .text("+").expression("(?:RESP|BUFF):GT[IV]G[NF],") .expression("(?:.{6}|.{10})?,") // protocol version .number("(d{15}|x{14}),") // imei .expression("[^,]*,") // device name @@ -1055,7 +1055,7 @@ private Object decodeIgn(Channel channel, SocketAddress remoteAddress, String se decodeLocation(position, parser); - position.set(Position.KEY_IGNITION, sentence.contains("IGN")); + position.set(Position.KEY_IGNITION, sentence.contains("GN")); position.set(Position.KEY_HOURS, parseHours(parser.next())); position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000); @@ -1677,6 +1677,8 @@ protected Object decode( break; case "IGN": case "IGF": + case "VGN": + case "VGF": result = decodeIgn(channel, remoteAddress, sentence); break; case "LSW": diff --git a/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java index bc1bee229bc..4cccf8fa2de 100644 --- a/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java @@ -11,6 +11,14 @@ public void testDecode() throws Exception { var decoder = inject(new Gl200TextProtocolDecoder(null)); + verifyAttribute(decoder, buffer( + "+RESP:GTVGN,C2010D,869653060009939,GV600M,453966,0,0.0,0,163.9,10.239379,45.931389,20231210233723,0222,0010,2F31,006D7621,,,0.0,20240107182623,143F$"), + Position.KEY_IGNITION, true); + + verifyAttribute(decoder, buffer( + "+RESP:GTIGF,8020050502,867488060278727,,509,1,0.0,184,32.6,14.003446,42.654554,20240219124629,0222,0001,0FAD,0497F715,00,,0.0,20240219124630,FD2F$"), + Position.KEY_IGNITION, false); + verifyPositions(decoder, buffer( "+RESP:GTERI,C2010D,869653060009939,GV600M,00000100,,10,1,0,0.0,0,163.9,10.239379,45.931389,20231210233723,0222,0010,2F31,006D7621,,0.0,,,,3,410000,,1,0,6,4,0,,01BF,Sondaporte,EA7D0A3882F6,1,3484,21.81,43,00,00,0,0,0,20240114221802,1875$")); From ee996425221ca64fdf00777d9035cedc6dccfa43 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 30 Mar 2024 15:23:00 -0700 Subject: [PATCH 31/42] Improve time handler --- .../java/org/traccar/handler/TimeHandler.java | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/traccar/handler/TimeHandler.java b/src/main/java/org/traccar/handler/TimeHandler.java index 3c3e17450dc..ef0c3445d31 100644 --- a/src/main/java/org/traccar/handler/TimeHandler.java +++ b/src/main/java/org/traccar/handler/TimeHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2019 - 2024 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. @@ -18,13 +18,12 @@ import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; -import org.traccar.BaseProtocolDecoder; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.model.Position; -import jakarta.inject.Inject; -import jakarta.inject.Singleton; import java.util.Arrays; import java.util.HashSet; import java.util.Set; @@ -56,17 +55,16 @@ public TimeHandler(Config config) { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { - if (enabled && msg instanceof Position && (protocols == null - || protocols.contains(ctx.pipeline().get(BaseProtocolDecoder.class).getProtocolName()))) { - + if (enabled && msg instanceof Position) { Position position = (Position) msg; - if (useServerTime) { - position.setDeviceTime(position.getServerTime()); - position.setFixTime(position.getServerTime()); - } else { - position.setFixTime(position.getDeviceTime()); + if (protocols == null || protocols.contains(position.getProtocol())) { + if (useServerTime) { + position.setDeviceTime(position.getServerTime()); + position.setFixTime(position.getServerTime()); + } else { + position.setFixTime(position.getDeviceTime()); + } } - } ctx.fireChannelRead(msg); } From f84e2710e05660822633ec9e61cde44c03a42d7e Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 30 Mar 2024 18:46:37 -0700 Subject: [PATCH 32/42] Refactor position and event handlers --- debug.xml | 1 + .../java/org/traccar/BaseDataHandler.java | 38 ----- .../java/org/traccar/BasePipelineFactory.java | 97 +++--------- .../org/traccar/ExtendedObjectDecoder.java | 2 +- .../java/org/traccar/ProcessingHandler.java | 145 ++++++++++++++++++ .../traccar/handler/BasePositionHandler.java | 27 ++++ .../handler/ComputedAttributesHandler.java | 40 ++--- .../handler/CopyAttributesHandler.java | 16 +- ...tDataHandler.java => DatabaseHandler.java} | 20 +-- .../org/traccar/handler/DistanceHandler.java | 13 +- .../traccar/handler/EngineHoursHandler.java | 16 +- .../org/traccar/handler/FilterHandler.java | 25 +-- .../org/traccar/handler/GeocoderHandler.java | 65 ++++---- .../org/traccar/handler/GeofenceHandler.java | 15 +- .../traccar/handler/GeolocationHandler.java | 73 ++++----- .../traccar/handler/HemisphereHandler.java | 16 +- .../org/traccar/handler/MotionHandler.java | 16 +- .../PositionForwardingHandler.java | 18 +-- .../traccar/handler/SpeedLimitHandler.java | 49 +++--- .../java/org/traccar/handler/TimeHandler.java | 15 +- .../handler/events/AlertEventHandler.java | 17 +- .../handler/events/BaseEventHandler.java | 32 +--- .../handler/events/BehaviorEventHandler.java | 18 +-- .../events/CommandResultEventHandler.java | 17 +- .../handler/events/DriverEventHandler.java | 18 +-- .../handler/events/FuelEventHandler.java | 22 +-- .../handler/events/GeofenceEventHandler.java | 21 +-- .../handler/events/IgnitionEventHandler.java | 25 +-- .../events/MaintenanceEventHandler.java | 19 +-- .../handler/events/MediaEventHandler.java | 16 +- .../handler/events/MotionEventHandler.java | 22 +-- .../handler/events/OverspeedEventHandler.java | 27 ++-- .../{ => network}/AcknowledgementHandler.java | 2 +- .../network}/MainEventHandler.java | 5 +- .../NetworkForwarderHandler.java | 2 +- .../{ => network}/NetworkMessageHandler.java | 2 +- .../{ => network}/OpenChannelHandler.java | 2 +- .../{ => network}/RemoteAddressHandler.java | 8 +- .../{ => network}/StandardLoggingHandler.java | 2 +- .../traccar/handler/DistanceHandlerTest.java | 5 +- .../traccar/handler/MotionHandlerTest.java | 3 +- .../handler/events/AlertEventHandlerTest.java | 12 +- .../events/CommandResultEventHandlerTest.java | 10 +- .../events/IgnitionEventHandlerTest.java | 8 +- .../events/MaintenanceEventHandlerTest.java | 25 ++- 45 files changed, 467 insertions(+), 580 deletions(-) delete mode 100644 src/main/java/org/traccar/BaseDataHandler.java create mode 100644 src/main/java/org/traccar/ProcessingHandler.java create mode 100644 src/main/java/org/traccar/handler/BasePositionHandler.java rename src/main/java/org/traccar/handler/{DefaultDataHandler.java => DatabaseHandler.java} (74%) rename src/main/java/org/traccar/{ => handler}/PositionForwardingHandler.java (92%) rename src/main/java/org/traccar/handler/{ => network}/AcknowledgementHandler.java (99%) rename src/main/java/org/traccar/{ => handler/network}/MainEventHandler.java (98%) rename src/main/java/org/traccar/handler/{ => network}/NetworkForwarderHandler.java (98%) rename src/main/java/org/traccar/handler/{ => network}/NetworkMessageHandler.java (98%) rename src/main/java/org/traccar/handler/{ => network}/OpenChannelHandler.java (97%) rename src/main/java/org/traccar/handler/{ => network}/RemoteAddressHandler.java (92%) rename src/main/java/org/traccar/handler/{ => network}/StandardLoggingHandler.java (98%) diff --git a/debug.xml b/debug.xml index 2569bb8cd90..028e9210b1a 100644 --- a/debug.xml +++ b/debug.xml @@ -16,6 +16,7 @@ true false + true true diff --git a/src/main/java/org/traccar/BaseDataHandler.java b/src/main/java/org/traccar/BaseDataHandler.java deleted file mode 100644 index 48794b0d71b..00000000000 --- a/src/main/java/org/traccar/BaseDataHandler.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2015 - 2018 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; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; -import org.traccar.model.Position; - -public abstract class BaseDataHandler extends ChannelInboundHandlerAdapter { - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof Position) { - Position position = handlePosition((Position) msg); - if (position != null) { - ctx.fireChannelRead(position); - } - } else { - super.channelRead(ctx, msg); - } - } - - protected abstract Position handlePosition(Position position); - -} diff --git a/src/main/java/org/traccar/BasePipelineFactory.java b/src/main/java/org/traccar/BasePipelineFactory.java index ca4a4ae6322..6d9431ad629 100644 --- a/src/main/java/org/traccar/BasePipelineFactory.java +++ b/src/main/java/org/traccar/BasePipelineFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2023 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2024 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. @@ -25,36 +25,13 @@ import io.netty.handler.timeout.IdleStateHandler; import org.traccar.config.Config; import org.traccar.config.Keys; -import org.traccar.handler.AcknowledgementHandler; -import org.traccar.handler.ComputedAttributesHandler; -import org.traccar.handler.CopyAttributesHandler; -import org.traccar.handler.DefaultDataHandler; -import org.traccar.handler.DistanceHandler; -import org.traccar.handler.EngineHoursHandler; -import org.traccar.handler.FilterHandler; -import org.traccar.handler.GeocoderHandler; -import org.traccar.handler.GeofenceHandler; -import org.traccar.handler.GeolocationHandler; -import org.traccar.handler.HemisphereHandler; -import org.traccar.handler.MotionHandler; -import org.traccar.handler.NetworkForwarderHandler; -import org.traccar.handler.NetworkMessageHandler; -import org.traccar.handler.OpenChannelHandler; -import org.traccar.handler.RemoteAddressHandler; -import org.traccar.handler.SpeedLimitHandler; -import org.traccar.handler.StandardLoggingHandler; -import org.traccar.handler.TimeHandler; -import org.traccar.handler.events.AlertEventHandler; -import org.traccar.handler.events.BehaviorEventHandler; -import org.traccar.handler.events.CommandResultEventHandler; -import org.traccar.handler.events.DriverEventHandler; -import org.traccar.handler.events.FuelEventHandler; -import org.traccar.handler.events.GeofenceEventHandler; -import org.traccar.handler.events.IgnitionEventHandler; -import org.traccar.handler.events.MaintenanceEventHandler; -import org.traccar.handler.events.MediaEventHandler; -import org.traccar.handler.events.MotionEventHandler; -import org.traccar.handler.events.OverspeedEventHandler; +import org.traccar.handler.network.AcknowledgementHandler; +import org.traccar.handler.network.MainEventHandler; +import org.traccar.handler.network.NetworkForwarderHandler; +import org.traccar.handler.network.NetworkMessageHandler; +import org.traccar.handler.network.OpenChannelHandler; +import org.traccar.handler.network.RemoteAddressHandler; +import org.traccar.handler.network.StandardLoggingHandler; import java.util.Map; @@ -83,15 +60,7 @@ public BasePipelineFactory(TrackerConnector connector, Config config, String pro protected abstract void addProtocolHandlers(PipelineBuilder pipeline); - @SafeVarargs - private void addHandlers(ChannelPipeline pipeline, Class... handlerClasses) { - for (Class handlerClass : handlerClasses) { - if (handlerClass != null) { - pipeline.addLast(injector.getInstance(handlerClass)); - } - } - } - + @SuppressWarnings("unchecked") public static T getHandler(ChannelPipeline pipeline, Class clazz) { for (Map.Entry handlerEntry : pipeline) { ChannelHandler handler = handlerEntry.getValue(); @@ -107,6 +76,11 @@ public static T getHandler(ChannelPipeline pipeline, return null; } + private T injectMembers(T object) { + injector.injectMembers(object); + return object; + } + @Override protected void initChannel(Channel channel) { final ChannelPipeline pipeline = channel.pipeline(); @@ -119,15 +93,11 @@ protected void initChannel(Channel channel) { pipeline.addLast(new OpenChannelHandler(connector)); if (config.hasKey(Keys.SERVER_FORWARD)) { int port = config.getInteger(Keys.PROTOCOL_PORT.withPrefix(protocol)); - var handler = new NetworkForwarderHandler(port); - injector.injectMembers(handler); - pipeline.addLast(handler); + pipeline.addLast(injectMembers(new NetworkForwarderHandler(port))); } pipeline.addLast(new NetworkMessageHandler()); - - var loggingHandler = new StandardLoggingHandler(protocol); - injector.injectMembers(loggingHandler); - pipeline.addLast(loggingHandler); + pipeline.addLast(new RemoteAddressHandler(config)); + pipeline.addLast(injectMembers(new StandardLoggingHandler(protocol))); if (!connector.isDatagram() && !config.getBoolean(Keys.SERVER_INSTANT_ACKNOWLEDGEMENT)) { pipeline.addLast(new AcknowledgementHandler()); @@ -135,7 +105,7 @@ protected void initChannel(Channel channel) { addProtocolHandlers(handler -> { if (handler instanceof BaseProtocolDecoder || handler instanceof BaseProtocolEncoder) { - injector.injectMembers(handler); + injectMembers(handler); } else { if (handler instanceof ChannelInboundHandler) { handler = new WrapperInboundHandler((ChannelInboundHandler) handler); @@ -146,35 +116,8 @@ protected void initChannel(Channel channel) { pipeline.addLast(handler); }); - addHandlers( - pipeline, - TimeHandler.class, - GeolocationHandler.class, - HemisphereHandler.class, - DistanceHandler.class, - RemoteAddressHandler.class, - FilterHandler.class, - GeofenceHandler.class, - GeocoderHandler.class, - SpeedLimitHandler.class, - MotionHandler.class, - CopyAttributesHandler.class, - EngineHoursHandler.class, - ComputedAttributesHandler.class, - PositionForwardingHandler.class, - DefaultDataHandler.class, - MediaEventHandler.class, - CommandResultEventHandler.class, - OverspeedEventHandler.class, - BehaviorEventHandler.class, - FuelEventHandler.class, - MotionEventHandler.class, - GeofenceEventHandler.class, - AlertEventHandler.class, - IgnitionEventHandler.class, - MaintenanceEventHandler.class, - DriverEventHandler.class, - MainEventHandler.class); + pipeline.addLast(injector.getInstance(ProcessingHandler.class)); + pipeline.addLast(injector.getInstance(MainEventHandler.class)); } } diff --git a/src/main/java/org/traccar/ExtendedObjectDecoder.java b/src/main/java/org/traccar/ExtendedObjectDecoder.java index cddddcd8089..9468e2fffce 100644 --- a/src/main/java/org/traccar/ExtendedObjectDecoder.java +++ b/src/main/java/org/traccar/ExtendedObjectDecoder.java @@ -23,7 +23,7 @@ import io.netty.util.ReferenceCountUtil; import org.traccar.config.Config; import org.traccar.config.Keys; -import org.traccar.handler.AcknowledgementHandler; +import org.traccar.handler.network.AcknowledgementHandler; import org.traccar.helper.DataConverter; import org.traccar.model.Position; diff --git a/src/main/java/org/traccar/ProcessingHandler.java b/src/main/java/org/traccar/ProcessingHandler.java new file mode 100644 index 00000000000..7627c719b19 --- /dev/null +++ b/src/main/java/org/traccar/ProcessingHandler.java @@ -0,0 +1,145 @@ +/* + * Copyright 2024 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; + +import com.google.inject.Injector; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import org.traccar.database.NotificationManager; +import org.traccar.handler.BasePositionHandler; +import org.traccar.handler.ComputedAttributesHandler; +import org.traccar.handler.CopyAttributesHandler; +import org.traccar.handler.DatabaseHandler; +import org.traccar.handler.DistanceHandler; +import org.traccar.handler.EngineHoursHandler; +import org.traccar.handler.FilterHandler; +import org.traccar.handler.GeocoderHandler; +import org.traccar.handler.GeofenceHandler; +import org.traccar.handler.GeolocationHandler; +import org.traccar.handler.HemisphereHandler; +import org.traccar.handler.MotionHandler; +import org.traccar.handler.PositionForwardingHandler; +import org.traccar.handler.SpeedLimitHandler; +import org.traccar.handler.TimeHandler; +import org.traccar.handler.events.AlertEventHandler; +import org.traccar.handler.events.BaseEventHandler; +import org.traccar.handler.events.BehaviorEventHandler; +import org.traccar.handler.events.CommandResultEventHandler; +import org.traccar.handler.events.DriverEventHandler; +import org.traccar.handler.events.FuelEventHandler; +import org.traccar.handler.events.GeofenceEventHandler; +import org.traccar.handler.events.IgnitionEventHandler; +import org.traccar.handler.events.MaintenanceEventHandler; +import org.traccar.handler.events.MediaEventHandler; +import org.traccar.handler.events.MotionEventHandler; +import org.traccar.handler.events.OverspeedEventHandler; +import org.traccar.handler.network.AcknowledgementHandler; +import org.traccar.model.Position; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Singleton +@ChannelHandler.Sharable +public class ProcessingHandler extends ChannelInboundHandlerAdapter { + + private final NotificationManager notificationManager; + private final List positionHandlers; + private final List eventHandlers; + + @Inject + public ProcessingHandler(Injector injector, NotificationManager notificationManager) { + this.notificationManager = notificationManager; + + positionHandlers = Stream.of( + TimeHandler.class, + GeolocationHandler.class, + HemisphereHandler.class, + DistanceHandler.class, + FilterHandler.class, + GeofenceHandler.class, + GeocoderHandler.class, + SpeedLimitHandler.class, + MotionHandler.class, + EngineHoursHandler.class, + ComputedAttributesHandler.class, + CopyAttributesHandler.class, + PositionForwardingHandler.class, + DatabaseHandler.class) + .map((clazz) -> (BasePositionHandler) injector.getInstance(clazz)) + .filter(Objects::nonNull) + .collect(Collectors.toUnmodifiableList()); + + eventHandlers = Stream.of( + MediaEventHandler.class, + CommandResultEventHandler.class, + OverspeedEventHandler.class, + BehaviorEventHandler.class, + FuelEventHandler.class, + MotionEventHandler.class, + GeofenceEventHandler.class, + AlertEventHandler.class, + IgnitionEventHandler.class, + MaintenanceEventHandler.class, + DriverEventHandler.class) + .map((clazz) -> (BaseEventHandler) injector.getInstance(clazz)) + .filter(Objects::nonNull) + .collect(Collectors.toUnmodifiableList()); + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + if (msg instanceof Position) { + processPositionHandlers(ctx, (Position) msg); + } + super.channelRead(ctx, msg); + } + + private void processPositionHandlers(ChannelHandlerContext ctx, Position position) { + var iterator = positionHandlers.iterator(); + iterator.next().handlePosition(position, new BasePositionHandler.Callback() { + @Override + public void processed(Position position) { + if (position != null) { + if (iterator.hasNext()) { + iterator.next().handlePosition(position, this); + } else { + processEventHandlers(ctx, position); + } + } else { + finishedProcessing(ctx, null); + } + } + }); + } + + private void processEventHandlers(ChannelHandlerContext ctx, Position position) { + eventHandlers.forEach(handler -> handler.analyzePosition( + position, (event) -> notificationManager.updateEvents(Map.of(event, position)))); + finishedProcessing(ctx, position); + } + + private void finishedProcessing(ChannelHandlerContext ctx, Position position) { + ctx.writeAndFlush(new AcknowledgementHandler.EventHandled(position)); + } + +} diff --git a/src/main/java/org/traccar/handler/BasePositionHandler.java b/src/main/java/org/traccar/handler/BasePositionHandler.java new file mode 100644 index 00000000000..2fee5c65265 --- /dev/null +++ b/src/main/java/org/traccar/handler/BasePositionHandler.java @@ -0,0 +1,27 @@ +/* + * Copyright 2015 - 2024 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.handler; + +import org.traccar.model.Position; + +public abstract class BasePositionHandler { + + public interface Callback { + void processed(Position position); + } + + public abstract void handlePosition(Position position, Callback callback); +} diff --git a/src/main/java/org/traccar/handler/ComputedAttributesHandler.java b/src/main/java/org/traccar/handler/ComputedAttributesHandler.java index 8b010ceae55..8d6fb39c3d4 100644 --- a/src/main/java/org/traccar/handler/ComputedAttributesHandler.java +++ b/src/main/java/org/traccar/handler/ComputedAttributesHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2023 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2024 Anton Tananaev (anton@traccar.org) * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,26 +16,15 @@ */ package org.traccar.handler; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.List; - -import io.netty.channel.ChannelHandler; -import org.apache.commons.jexl3.JexlFeatures; -import org.apache.commons.jexl3.JexlEngine; +import jakarta.inject.Inject; import org.apache.commons.jexl3.JexlBuilder; -import org.apache.commons.jexl3.introspection.JexlSandbox; +import org.apache.commons.jexl3.JexlEngine; import org.apache.commons.jexl3.JexlException; +import org.apache.commons.jexl3.JexlFeatures; import org.apache.commons.jexl3.MapContext; +import org.apache.commons.jexl3.introspection.JexlSandbox; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.traccar.BaseDataHandler; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.model.Attribute; @@ -43,12 +32,17 @@ import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import jakarta.inject.Inject; -import jakarta.inject.Singleton; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; -@Singleton -@ChannelHandler.Sharable -public class ComputedAttributesHandler extends BaseDataHandler { +public class ComputedAttributesHandler extends BasePositionHandler { private static final Logger LOGGER = LoggerFactory.getLogger(ComputedAttributesHandler.class); @@ -144,7 +138,7 @@ public Object computeAttribute(Attribute attribute, Position position) throws Je } @Override - protected Position handlePosition(Position position) { + public void handlePosition(Position position, Callback callback) { Collection attributes = cacheManager.getDeviceObjects(position.getDeviceId(), Attribute.class); for (Attribute attribute : attributes) { if (attribute.getAttribute() != null) { @@ -202,7 +196,7 @@ protected Position handlePosition(Position position) { } } } - return position; + callback.processed(position); } } diff --git a/src/main/java/org/traccar/handler/CopyAttributesHandler.java b/src/main/java/org/traccar/handler/CopyAttributesHandler.java index 42b438e4137..9c31bf56e16 100644 --- a/src/main/java/org/traccar/handler/CopyAttributesHandler.java +++ b/src/main/java/org/traccar/handler/CopyAttributesHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2024 Anton Tananaev (anton@traccar.org) * Copyright 2016 - 2017 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,20 +16,14 @@ */ package org.traccar.handler; -import io.netty.channel.ChannelHandler; -import org.traccar.BaseDataHandler; +import jakarta.inject.Inject; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.helper.model.AttributeUtil; import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import jakarta.inject.Inject; -import jakarta.inject.Singleton; - -@Singleton -@ChannelHandler.Sharable -public class CopyAttributesHandler extends BaseDataHandler { +public class CopyAttributesHandler extends BasePositionHandler { private final boolean enabled; private final CacheManager cacheManager; @@ -41,7 +35,7 @@ public CopyAttributesHandler(Config config, CacheManager cacheManager) { } @Override - protected Position handlePosition(Position position) { + public void handlePosition(Position position, Callback callback) { if (enabled) { String attributesString = AttributeUtil.lookup( cacheManager, Keys.PROCESSING_COPY_ATTRIBUTES, position.getDeviceId()); @@ -54,7 +48,7 @@ protected Position handlePosition(Position position) { } } } - return position; + callback.processed(position); } } diff --git a/src/main/java/org/traccar/handler/DefaultDataHandler.java b/src/main/java/org/traccar/handler/DatabaseHandler.java similarity index 74% rename from src/main/java/org/traccar/handler/DefaultDataHandler.java rename to src/main/java/org/traccar/handler/DatabaseHandler.java index cca6dcd0ad4..b1f218a1e19 100644 --- a/src/main/java/org/traccar/handler/DefaultDataHandler.java +++ b/src/main/java/org/traccar/handler/DatabaseHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2024 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. @@ -15,33 +15,27 @@ */ package org.traccar.handler; -import io.netty.channel.ChannelHandler; +import jakarta.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.traccar.BaseDataHandler; import org.traccar.model.Position; import org.traccar.storage.Storage; import org.traccar.storage.query.Columns; import org.traccar.storage.query.Request; -import jakarta.inject.Inject; -import jakarta.inject.Singleton; - -@Singleton -@ChannelHandler.Sharable -public class DefaultDataHandler extends BaseDataHandler { +public class DatabaseHandler extends BasePositionHandler { - private static final Logger LOGGER = LoggerFactory.getLogger(DefaultDataHandler.class); + private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseHandler.class); private final Storage storage; @Inject - public DefaultDataHandler(Storage storage) { + public DatabaseHandler(Storage storage) { this.storage = storage; } @Override - protected Position handlePosition(Position position) { + public void handlePosition(Position position, Callback callback) { try { position.setId(storage.addObject(position, new Request(new Columns.Exclude("id")))); @@ -49,7 +43,7 @@ protected Position handlePosition(Position position) { LOGGER.warn("Failed to store position", error); } - return position; + callback.processed(position); } } diff --git a/src/main/java/org/traccar/handler/DistanceHandler.java b/src/main/java/org/traccar/handler/DistanceHandler.java index db8c7377971..ee5d6489417 100644 --- a/src/main/java/org/traccar/handler/DistanceHandler.java +++ b/src/main/java/org/traccar/handler/DistanceHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2023 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2024 Anton Tananaev (anton@traccar.org) * Copyright 2015 Amila Silva * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,19 +16,14 @@ */ package org.traccar.handler; -import io.netty.channel.ChannelHandler; import jakarta.inject.Inject; -import jakarta.inject.Singleton; -import org.traccar.BaseDataHandler; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.helper.DistanceCalculator; import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -@Singleton -@ChannelHandler.Sharable -public class DistanceHandler extends BaseDataHandler { +public class DistanceHandler extends BasePositionHandler { private final CacheManager cacheManager; @@ -45,7 +40,7 @@ public DistanceHandler(Config config, CacheManager cacheManager) { } @Override - protected Position handlePosition(Position position) { + public void handlePosition(Position position, Callback callback) { double distance = 0.0; if (position.hasAttribute(Position.KEY_DISTANCE)) { @@ -76,7 +71,7 @@ protected Position handlePosition(Position position) { position.set(Position.KEY_DISTANCE, distance); position.set(Position.KEY_TOTAL_DISTANCE, totalDistance + distance); - return position; + callback.processed(position); } } diff --git a/src/main/java/org/traccar/handler/EngineHoursHandler.java b/src/main/java/org/traccar/handler/EngineHoursHandler.java index 621205b34f3..ed5f9b50921 100644 --- a/src/main/java/org/traccar/handler/EngineHoursHandler.java +++ b/src/main/java/org/traccar/handler/EngineHoursHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2018 - 2024 Anton Tananaev (anton@traccar.org) * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,17 +16,11 @@ */ package org.traccar.handler; -import io.netty.channel.ChannelHandler; -import org.traccar.BaseDataHandler; +import jakarta.inject.Inject; import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import jakarta.inject.Inject; -import jakarta.inject.Singleton; - -@Singleton -@ChannelHandler.Sharable -public class EngineHoursHandler extends BaseDataHandler { +public class EngineHoursHandler extends BasePositionHandler { private final CacheManager cacheManager; @@ -36,7 +30,7 @@ public EngineHoursHandler(CacheManager cacheManager) { } @Override - protected Position handlePosition(Position position) { + public void handlePosition(Position position, Callback callback) { if (!position.hasAttribute(Position.KEY_HOURS)) { Position last = cacheManager.getPosition(position.getDeviceId()); if (last != null) { @@ -49,7 +43,7 @@ protected Position handlePosition(Position position) { } } } - return position; + callback.processed(position); } } diff --git a/src/main/java/org/traccar/handler/FilterHandler.java b/src/main/java/org/traccar/handler/FilterHandler.java index a15d3ffad0c..4cc6233d0f4 100644 --- a/src/main/java/org/traccar/handler/FilterHandler.java +++ b/src/main/java/org/traccar/handler/FilterHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 - 2023 Anton Tananaev (anton@traccar.org) + * Copyright 2014 - 2024 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. @@ -15,9 +15,7 @@ */ package org.traccar.handler; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; +import jakarta.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.config.Config; @@ -36,13 +34,9 @@ import org.traccar.storage.query.Order; import org.traccar.storage.query.Request; -import jakarta.inject.Inject; -import jakarta.inject.Singleton; import java.util.Date; -@Singleton -@ChannelHandler.Sharable -public class FilterHandler extends ChannelInboundHandlerAdapter { +public class FilterHandler extends BasePositionHandler { private static final Logger LOGGER = LoggerFactory.getLogger(FilterHandler.class); @@ -277,16 +271,11 @@ protected boolean filter(Position position) { } @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof Position) { - Position position = (Position) msg; - if (enabled && filter(position)) { - ctx.writeAndFlush(new AcknowledgementHandler.EventHandled(position)); - } else { - ctx.fireChannelRead(position); - } + public void handlePosition(Position position, Callback callback) { + if (enabled && filter(position)) { + callback.processed(null); } else { - super.channelRead(ctx, msg); + callback.processed(position); } } diff --git a/src/main/java/org/traccar/handler/GeocoderHandler.java b/src/main/java/org/traccar/handler/GeocoderHandler.java index e4f240a9082..c62bcb6f8b5 100644 --- a/src/main/java/org/traccar/handler/GeocoderHandler.java +++ b/src/main/java/org/traccar/handler/GeocoderHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2024 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. @@ -15,9 +15,6 @@ */ package org.traccar.handler; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.config.Config; @@ -26,8 +23,7 @@ import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -@ChannelHandler.Sharable -public class GeocoderHandler extends ChannelInboundHandlerAdapter { +public class GeocoderHandler extends BasePositionHandler { private static final Logger LOGGER = LoggerFactory.getLogger(GeocoderHandler.class); @@ -46,39 +42,38 @@ public GeocoderHandler(Config config, Geocoder geocoder, CacheManager cacheManag } @Override - public void channelRead(final ChannelHandlerContext ctx, Object message) { - if (message instanceof Position && !ignorePositions) { - final Position position = (Position) message; - if (processInvalidPositions || position.getValid()) { - if (reuseDistance != 0) { - Position lastPosition = cacheManager.getPosition(position.getDeviceId()); - if (lastPosition != null && lastPosition.getAddress() != null - && position.getDouble(Position.KEY_DISTANCE) <= reuseDistance) { - position.setAddress(lastPosition.getAddress()); - ctx.fireChannelRead(position); - return; - } + public void handlePosition(Position position, Callback callback) { + if (!ignorePositions) { + callback.processed(position); + } + + if (processInvalidPositions || position.getValid()) { + if (reuseDistance != 0) { + Position lastPosition = cacheManager.getPosition(position.getDeviceId()); + if (lastPosition != null && lastPosition.getAddress() != null + && position.getDouble(Position.KEY_DISTANCE) <= reuseDistance) { + position.setAddress(lastPosition.getAddress()); + callback.processed(position); + return; } + } - geocoder.getAddress(position.getLatitude(), position.getLongitude(), - new Geocoder.ReverseGeocoderCallback() { - @Override - public void onSuccess(String address) { - position.setAddress(address); - ctx.fireChannelRead(position); - } + geocoder.getAddress(position.getLatitude(), position.getLongitude(), + new Geocoder.ReverseGeocoderCallback() { + @Override + public void onSuccess(String address) { + position.setAddress(address); + callback.processed(position); + } - @Override - public void onFailure(Throwable e) { - LOGGER.warn("Geocoding failed", e); - ctx.fireChannelRead(position); - } - }); - } else { - ctx.fireChannelRead(position); - } + @Override + public void onFailure(Throwable e) { + LOGGER.warn("Geocoding failed", e); + callback.processed(position); + } + }); } else { - ctx.fireChannelRead(message); + callback.processed(position); } } diff --git a/src/main/java/org/traccar/handler/GeofenceHandler.java b/src/main/java/org/traccar/handler/GeofenceHandler.java index 68bc6dbf0ba..33b46f058bc 100644 --- a/src/main/java/org/traccar/handler/GeofenceHandler.java +++ b/src/main/java/org/traccar/handler/GeofenceHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Anton Tananaev (anton@traccar.org) + * Copyright 2023 - 2024 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. @@ -15,20 +15,15 @@ */ package org.traccar.handler; -import io.netty.channel.ChannelHandler; -import org.traccar.BaseDataHandler; +import jakarta.inject.Inject; import org.traccar.config.Config; import org.traccar.helper.model.GeofenceUtil; import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import jakarta.inject.Inject; -import jakarta.inject.Singleton; import java.util.List; -@Singleton -@ChannelHandler.Sharable -public class GeofenceHandler extends BaseDataHandler { +public class GeofenceHandler extends BasePositionHandler { private final Config config; private final CacheManager cacheManager; @@ -40,13 +35,13 @@ public GeofenceHandler(Config config, CacheManager cacheManager) { } @Override - protected Position handlePosition(Position position) { + public void handlePosition(Position position, Callback callback) { List geofenceIds = GeofenceUtil.getCurrentGeofences(config, cacheManager, position); if (!geofenceIds.isEmpty()) { position.setGeofenceIds(geofenceIds); } - return position; + callback.processed(position); } } diff --git a/src/main/java/org/traccar/handler/GeolocationHandler.java b/src/main/java/org/traccar/handler/GeolocationHandler.java index a54ea03e3b7..cb9c04808f8 100644 --- a/src/main/java/org/traccar/handler/GeolocationHandler.java +++ b/src/main/java/org/traccar/handler/GeolocationHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2023 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2024 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. @@ -15,9 +15,6 @@ */ package org.traccar.handler; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.config.Config; @@ -27,8 +24,7 @@ import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -@ChannelHandler.Sharable -public class GeolocationHandler extends ChannelInboundHandlerAdapter { +public class GeolocationHandler extends BasePositionHandler { private static final Logger LOGGER = LoggerFactory.getLogger(GeolocationHandler.class); @@ -51,46 +47,41 @@ public GeolocationHandler( } @Override - public void channelRead(final ChannelHandlerContext ctx, Object message) { - if (message instanceof Position) { - final Position position = (Position) message; - if ((position.getOutdated() || processInvalidPositions && !position.getValid()) - && position.getNetwork() != null - && (!requireWifi || position.getNetwork().getWifiAccessPoints() != null)) { - if (reuse) { - Position lastPosition = cacheManager.getPosition(position.getDeviceId()); - if (lastPosition != null && position.getNetwork().equals(lastPosition.getNetwork())) { - updatePosition( - position, lastPosition.getLatitude(), lastPosition.getLongitude(), - lastPosition.getAccuracy()); - ctx.fireChannelRead(position); - return; - } + public void handlePosition(Position position, Callback callback) { + if ((position.getOutdated() || processInvalidPositions && !position.getValid()) + && position.getNetwork() != null + && (!requireWifi || position.getNetwork().getWifiAccessPoints() != null)) { + if (reuse) { + Position lastPosition = cacheManager.getPosition(position.getDeviceId()); + if (lastPosition != null && position.getNetwork().equals(lastPosition.getNetwork())) { + updatePosition( + position, lastPosition.getLatitude(), lastPosition.getLongitude(), + lastPosition.getAccuracy()); + callback.processed(position); + return; } + } - if (statisticsManager != null) { - statisticsManager.registerGeolocationRequest(); - } + if (statisticsManager != null) { + statisticsManager.registerGeolocationRequest(); + } - geolocationProvider.getLocation(position.getNetwork(), - new GeolocationProvider.LocationProviderCallback() { - @Override - public void onSuccess(double latitude, double longitude, double accuracy) { - updatePosition(position, latitude, longitude, accuracy); - ctx.fireChannelRead(position); - } + geolocationProvider.getLocation(position.getNetwork(), + new GeolocationProvider.LocationProviderCallback() { + @Override + public void onSuccess(double latitude, double longitude, double accuracy) { + updatePosition(position, latitude, longitude, accuracy); + callback.processed(position); + } - @Override - public void onFailure(Throwable e) { - LOGGER.warn("Geolocation network error", e); - ctx.fireChannelRead(position); - } - }); - } else { - ctx.fireChannelRead(position); - } + @Override + public void onFailure(Throwable e) { + LOGGER.warn("Geolocation network error", e); + callback.processed(position); + } + }); } else { - ctx.fireChannelRead(message); + callback.processed(position); } } diff --git a/src/main/java/org/traccar/handler/HemisphereHandler.java b/src/main/java/org/traccar/handler/HemisphereHandler.java index 294e449dbae..6b64177e450 100644 --- a/src/main/java/org/traccar/handler/HemisphereHandler.java +++ b/src/main/java/org/traccar/handler/HemisphereHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2024 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. @@ -15,18 +15,12 @@ */ package org.traccar.handler; -import io.netty.channel.ChannelHandler; -import org.traccar.BaseDataHandler; +import jakarta.inject.Inject; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.model.Position; -import jakarta.inject.Inject; -import jakarta.inject.Singleton; - -@Singleton -@ChannelHandler.Sharable -public class HemisphereHandler extends BaseDataHandler { +public class HemisphereHandler extends BasePositionHandler { private int latitudeFactor; private int longitudeFactor; @@ -52,14 +46,14 @@ public HemisphereHandler(Config config) { } @Override - protected Position handlePosition(Position position) { + public void handlePosition(Position position, Callback callback) { if (latitudeFactor != 0) { position.setLatitude(Math.abs(position.getLatitude()) * latitudeFactor); } if (longitudeFactor != 0) { position.setLongitude(Math.abs(position.getLongitude()) * longitudeFactor); } - return position; + callback.processed(position); } } diff --git a/src/main/java/org/traccar/handler/MotionHandler.java b/src/main/java/org/traccar/handler/MotionHandler.java index 68a31a16a04..bb7ff2a6523 100644 --- a/src/main/java/org/traccar/handler/MotionHandler.java +++ b/src/main/java/org/traccar/handler/MotionHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2023 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2024 Anton Tananaev (anton@traccar.org) * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,19 +16,13 @@ */ package org.traccar.handler; -import io.netty.channel.ChannelHandler; -import org.traccar.BaseDataHandler; +import jakarta.inject.Inject; import org.traccar.config.Keys; import org.traccar.helper.model.AttributeUtil; import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import jakarta.inject.Inject; -import jakarta.inject.Singleton; - -@Singleton -@ChannelHandler.Sharable -public class MotionHandler extends BaseDataHandler { +public class MotionHandler extends BasePositionHandler { private final CacheManager cacheManager; @@ -38,13 +32,13 @@ public MotionHandler(CacheManager cacheManager) { } @Override - protected Position handlePosition(Position position) { + public void handlePosition(Position position, Callback callback) { if (!position.hasAttribute(Position.KEY_MOTION)) { double threshold = AttributeUtil.lookup( cacheManager, Keys.EVENT_MOTION_SPEED_THRESHOLD, position.getDeviceId()); position.set(Position.KEY_MOTION, position.getSpeed() > threshold); } - return position; + callback.processed(position); } } diff --git a/src/main/java/org/traccar/PositionForwardingHandler.java b/src/main/java/org/traccar/handler/PositionForwardingHandler.java similarity index 92% rename from src/main/java/org/traccar/PositionForwardingHandler.java rename to src/main/java/org/traccar/handler/PositionForwardingHandler.java index a79b013671b..be62fff3767 100644 --- a/src/main/java/org/traccar/PositionForwardingHandler.java +++ b/src/main/java/org/traccar/handler/PositionForwardingHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2024 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. @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.traccar; +package org.traccar.handler; -import io.netty.channel.ChannelHandler; import io.netty.util.Timeout; import io.netty.util.Timer; import io.netty.util.TimerTask; +import jakarta.annotation.Nullable; +import jakarta.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.config.Config; @@ -30,15 +31,10 @@ import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import jakarta.annotation.Nullable; -import jakarta.inject.Inject; -import jakarta.inject.Singleton; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -@Singleton -@ChannelHandler.Sharable -public class PositionForwardingHandler extends BaseDataHandler { +public class PositionForwardingHandler extends BasePositionHandler { private static final Logger LOGGER = LoggerFactory.getLogger(PositionForwardingHandler.class); @@ -128,14 +124,14 @@ public void run(Timeout timeout) { } @Override - protected Position handlePosition(Position position) { + public void handlePosition(Position position, Callback callback) { if (positionForwarder != null) { PositionData positionData = new PositionData(); positionData.setPosition(position); positionData.setDevice(cacheManager.getObject(Device.class, position.getDeviceId())); new AsyncRequestAndCallback(positionData).send(); } - return position; + callback.processed(position); } } diff --git a/src/main/java/org/traccar/handler/SpeedLimitHandler.java b/src/main/java/org/traccar/handler/SpeedLimitHandler.java index 6edb6e91242..604c10ca71d 100644 --- a/src/main/java/org/traccar/handler/SpeedLimitHandler.java +++ b/src/main/java/org/traccar/handler/SpeedLimitHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2020 - 2024 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. @@ -15,20 +15,13 @@ */ package org.traccar.handler; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; +import jakarta.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.model.Position; import org.traccar.speedlimit.SpeedLimitProvider; -import jakarta.inject.Inject; -import jakarta.inject.Singleton; - -@Singleton -@ChannelHandler.Sharable -public class SpeedLimitHandler extends ChannelInboundHandlerAdapter { +public class SpeedLimitHandler extends BasePositionHandler { private static final Logger LOGGER = LoggerFactory.getLogger(SpeedLimitHandler.class); @@ -40,26 +33,22 @@ public SpeedLimitHandler(SpeedLimitProvider speedLimitProvider) { } @Override - public void channelRead(final ChannelHandlerContext ctx, Object message) { - if (message instanceof Position) { - final Position position = (Position) message; - speedLimitProvider.getSpeedLimit(position.getLatitude(), position.getLongitude(), - new SpeedLimitProvider.SpeedLimitProviderCallback() { - @Override - public void onSuccess(double speedLimit) { - position.set(Position.KEY_SPEED_LIMIT, speedLimit); - ctx.fireChannelRead(position); - } - - @Override - public void onFailure(Throwable e) { - LOGGER.warn("Speed limit provider failed", e); - ctx.fireChannelRead(position); - } - }); - } else { - ctx.fireChannelRead(message); - } + public void handlePosition(Position position, Callback callback) { + + speedLimitProvider.getSpeedLimit(position.getLatitude(), position.getLongitude(), + new SpeedLimitProvider.SpeedLimitProviderCallback() { + @Override + public void onSuccess(double speedLimit) { + position.set(Position.KEY_SPEED_LIMIT, speedLimit); + callback.processed(position); + } + + @Override + public void onFailure(Throwable e) { + LOGGER.warn("Speed limit provider failed", e); + callback.processed(position); + } + }); } } diff --git a/src/main/java/org/traccar/handler/TimeHandler.java b/src/main/java/org/traccar/handler/TimeHandler.java index ef0c3445d31..2e703c681b7 100644 --- a/src/main/java/org/traccar/handler/TimeHandler.java +++ b/src/main/java/org/traccar/handler/TimeHandler.java @@ -15,11 +15,7 @@ */ package org.traccar.handler; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; import jakarta.inject.Inject; -import jakarta.inject.Singleton; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.model.Position; @@ -28,9 +24,7 @@ import java.util.HashSet; import java.util.Set; -@Singleton -@ChannelHandler.Sharable -public class TimeHandler extends ChannelInboundHandlerAdapter { +public class TimeHandler extends BasePositionHandler { private final boolean enabled; private final boolean useServerTime; @@ -53,10 +47,9 @@ public TimeHandler(Config config) { } @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { + public void handlePosition(Position position, Callback callback) { - if (enabled && msg instanceof Position) { - Position position = (Position) msg; + if (enabled) { if (protocols == null || protocols.contains(position.getProtocol())) { if (useServerTime) { position.setDeviceTime(position.getServerTime()); @@ -66,7 +59,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { } } } - ctx.fireChannelRead(msg); + callback.processed(position); } } diff --git a/src/main/java/org/traccar/handler/events/AlertEventHandler.java b/src/main/java/org/traccar/handler/events/AlertEventHandler.java index 531a0f95717..ca580b60dc9 100644 --- a/src/main/java/org/traccar/handler/events/AlertEventHandler.java +++ b/src/main/java/org/traccar/handler/events/AlertEventHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2024 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. @@ -15,21 +15,13 @@ */ package org.traccar.handler.events; -import java.util.Collections; -import java.util.Map; - -import io.netty.channel.ChannelHandler; +import jakarta.inject.Inject; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.model.Event; import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import jakarta.inject.Inject; -import jakarta.inject.Singleton; - -@Singleton -@ChannelHandler.Sharable public class AlertEventHandler extends BaseEventHandler { private final CacheManager cacheManager; @@ -42,7 +34,7 @@ public AlertEventHandler(Config config, CacheManager cacheManager) { } @Override - protected Map analyzePosition(Position position) { + public void analyzePosition(Position position, Callback callback) { Object alarm = position.getAttributes().get(Position.KEY_ALARM); if (alarm != null) { boolean ignoreAlert = false; @@ -55,10 +47,9 @@ protected Map analyzePosition(Position position) { if (!ignoreAlert) { Event event = new Event(Event.TYPE_ALARM, position); event.set(Position.KEY_ALARM, (String) alarm); - return Collections.singletonMap(event, position); + callback.eventDetected(event); } } - return null; } } diff --git a/src/main/java/org/traccar/handler/events/BaseEventHandler.java b/src/main/java/org/traccar/handler/events/BaseEventHandler.java index 4a4fb40ff58..009c8314501 100644 --- a/src/main/java/org/traccar/handler/events/BaseEventHandler.java +++ b/src/main/java/org/traccar/handler/events/BaseEventHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2024 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. @@ -15,33 +15,17 @@ */ package org.traccar.handler.events; -import java.util.Map; - -import org.traccar.BaseDataHandler; -import org.traccar.database.NotificationManager; import org.traccar.model.Event; import org.traccar.model.Position; -import jakarta.inject.Inject; - -public abstract class BaseEventHandler extends BaseDataHandler { - - private NotificationManager notificationManager; +public abstract class BaseEventHandler { - @Inject - public void setNotificationManager(NotificationManager notificationManager) { - this.notificationManager = notificationManager; + public interface Callback { + void eventDetected(Event event); } - @Override - protected Position handlePosition(Position position) { - Map events = analyzePosition(position); - if (events != null && !events.isEmpty()) { - notificationManager.updateEvents(events); - } - return position; - } - - protected abstract Map analyzePosition(Position position); - + /** + * Event handlers should be processed synchronously. + */ + public abstract void analyzePosition(Position position, Callback callback); } diff --git a/src/main/java/org/traccar/handler/events/BehaviorEventHandler.java b/src/main/java/org/traccar/handler/events/BehaviorEventHandler.java index 08ae35fcd75..d654e18ce01 100644 --- a/src/main/java/org/traccar/handler/events/BehaviorEventHandler.java +++ b/src/main/java/org/traccar/handler/events/BehaviorEventHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2021 - 2024 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. @@ -15,7 +15,7 @@ */ package org.traccar.handler.events; -import io.netty.channel.ChannelHandler; +import jakarta.inject.Inject; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.helper.UnitsConverter; @@ -23,13 +23,6 @@ import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import jakarta.inject.Inject; -import jakarta.inject.Singleton; -import java.util.Collections; -import java.util.Map; - -@Singleton -@ChannelHandler.Sharable public class BehaviorEventHandler extends BaseEventHandler { private final double accelerationThreshold; @@ -45,7 +38,7 @@ public BehaviorEventHandler(Config config, CacheManager cacheManager) { } @Override - protected Map analyzePosition(Position position) { + public void analyzePosition(Position position, Callback callback) { Position lastPosition = cacheManager.getPosition(position.getDeviceId()); if (lastPosition != null && position.getFixTime().equals(lastPosition.getFixTime())) { @@ -54,14 +47,13 @@ protected Map analyzePosition(Position position) { if (accelerationThreshold != 0 && acceleration >= accelerationThreshold) { Event event = new Event(Event.TYPE_ALARM, position); event.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION); - return Collections.singletonMap(event, position); + callback.eventDetected(event); } else if (brakingThreshold != 0 && acceleration <= -brakingThreshold) { Event event = new Event(Event.TYPE_ALARM, position); event.set(Position.KEY_ALARM, Position.ALARM_BRAKING); - return Collections.singletonMap(event, position); + callback.eventDetected(event); } } - return null; } } diff --git a/src/main/java/org/traccar/handler/events/CommandResultEventHandler.java b/src/main/java/org/traccar/handler/events/CommandResultEventHandler.java index b70f8f33bc6..b98807b23ea 100644 --- a/src/main/java/org/traccar/handler/events/CommandResultEventHandler.java +++ b/src/main/java/org/traccar/handler/events/CommandResultEventHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2024 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. @@ -15,18 +15,10 @@ */ package org.traccar.handler.events; -import java.util.Collections; -import java.util.Map; - -import io.netty.channel.ChannelHandler; +import jakarta.inject.Inject; import org.traccar.model.Event; import org.traccar.model.Position; -import jakarta.inject.Inject; -import jakarta.inject.Singleton; - -@Singleton -@ChannelHandler.Sharable public class CommandResultEventHandler extends BaseEventHandler { @Inject @@ -34,14 +26,13 @@ public CommandResultEventHandler() { } @Override - protected Map analyzePosition(Position position) { + public void analyzePosition(Position position, Callback callback) { Object commandResult = position.getAttributes().get(Position.KEY_RESULT); if (commandResult != null) { Event event = new Event(Event.TYPE_COMMAND_RESULT, position); event.set(Position.KEY_RESULT, (String) commandResult); - return Collections.singletonMap(event, position); + callback.eventDetected(event); } - return null; } } diff --git a/src/main/java/org/traccar/handler/events/DriverEventHandler.java b/src/main/java/org/traccar/handler/events/DriverEventHandler.java index b683279835d..31f8d2b4bb4 100644 --- a/src/main/java/org/traccar/handler/events/DriverEventHandler.java +++ b/src/main/java/org/traccar/handler/events/DriverEventHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2024 Anton Tananaev (anton@traccar.org) * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,19 +16,12 @@ */ package org.traccar.handler.events; -import io.netty.channel.ChannelHandler; +import jakarta.inject.Inject; import org.traccar.helper.model.PositionUtil; import org.traccar.model.Event; import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import jakarta.inject.Inject; -import jakarta.inject.Singleton; -import java.util.Collections; -import java.util.Map; - -@Singleton -@ChannelHandler.Sharable public class DriverEventHandler extends BaseEventHandler { private final CacheManager cacheManager; @@ -39,9 +32,9 @@ public DriverEventHandler(CacheManager cacheManager) { } @Override - protected Map analyzePosition(Position position) { + public void analyzePosition(Position position, Callback callback) { if (!PositionUtil.isLatest(cacheManager, position)) { - return null; + return; } String driverUniqueId = position.getString(Position.KEY_DRIVER_UNIQUE_ID); if (driverUniqueId != null) { @@ -53,10 +46,9 @@ protected Map analyzePosition(Position position) { if (!driverUniqueId.equals(oldDriverUniqueId)) { Event event = new Event(Event.TYPE_DRIVER_CHANGED, position); event.set(Position.KEY_DRIVER_UNIQUE_ID, driverUniqueId); - return Collections.singletonMap(event, position); + callback.eventDetected(event); } } - return null; } } diff --git a/src/main/java/org/traccar/handler/events/FuelEventHandler.java b/src/main/java/org/traccar/handler/events/FuelEventHandler.java index e5085ecc2a3..c5675f51d31 100644 --- a/src/main/java/org/traccar/handler/events/FuelEventHandler.java +++ b/src/main/java/org/traccar/handler/events/FuelEventHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2023 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2024 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. @@ -15,7 +15,7 @@ */ package org.traccar.handler.events; -import io.netty.channel.ChannelHandler; +import jakarta.inject.Inject; import org.traccar.config.Keys; import org.traccar.helper.model.AttributeUtil; import org.traccar.helper.model.PositionUtil; @@ -24,12 +24,6 @@ import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import jakarta.inject.Inject; -import jakarta.inject.Singleton; -import java.util.Map; - -@Singleton -@ChannelHandler.Sharable public class FuelEventHandler extends BaseEventHandler { private final CacheManager cacheManager; @@ -40,14 +34,14 @@ public FuelEventHandler(CacheManager cacheManager) { } @Override - protected Map analyzePosition(Position position) { + public void analyzePosition(Position position, Callback callback) { Device device = cacheManager.getObject(Device.class, position.getDeviceId()); if (device == null) { - return null; + return; } if (!PositionUtil.isLatest(cacheManager, position)) { - return null; + return; } if (position.hasAttribute(Position.KEY_FUEL_LEVEL)) { @@ -61,19 +55,17 @@ protected Map analyzePosition(Position position) { double threshold = AttributeUtil.lookup( cacheManager, Keys.EVENT_FUEL_INCREASE_THRESHOLD, position.getDeviceId()); if (threshold > 0 && change >= threshold) { - return Map.of(new Event(Event.TYPE_DEVICE_FUEL_INCREASE, position), position); + callback.eventDetected(new Event(Event.TYPE_DEVICE_FUEL_INCREASE, position)); } } else if (change < 0) { double threshold = AttributeUtil.lookup( cacheManager, Keys.EVENT_FUEL_DROP_THRESHOLD, position.getDeviceId()); if (threshold > 0 && Math.abs(change) >= threshold) { - return Map.of(new Event(Event.TYPE_DEVICE_FUEL_DROP, position), position); + callback.eventDetected(new Event(Event.TYPE_DEVICE_FUEL_DROP, position)); } } } } - - return null; } } diff --git a/src/main/java/org/traccar/handler/events/GeofenceEventHandler.java b/src/main/java/org/traccar/handler/events/GeofenceEventHandler.java index dbe2b81182f..c8ecfb1ed46 100644 --- a/src/main/java/org/traccar/handler/events/GeofenceEventHandler.java +++ b/src/main/java/org/traccar/handler/events/GeofenceEventHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2023 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2024 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. @@ -15,7 +15,7 @@ */ package org.traccar.handler.events; -import io.netty.channel.ChannelHandler; +import jakarta.inject.Inject; import org.traccar.helper.model.PositionUtil; import org.traccar.model.Calendar; import org.traccar.model.Event; @@ -23,15 +23,9 @@ import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import jakarta.inject.Inject; -import jakarta.inject.Singleton; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; -@Singleton -@ChannelHandler.Sharable public class GeofenceEventHandler extends BaseEventHandler { private final CacheManager cacheManager; @@ -42,9 +36,9 @@ public GeofenceEventHandler(CacheManager cacheManager) { } @Override - protected Map analyzePosition(Position position) { + public void analyzePosition(Position position, Callback callback) { if (!PositionUtil.isLatest(cacheManager, position)) { - return null; + return; } List oldGeofences = new ArrayList<>(); @@ -60,7 +54,6 @@ protected Map analyzePosition(Position position) { oldGeofences.removeAll(position.getGeofenceIds()); } - Map events = new HashMap<>(); for (long geofenceId : oldGeofences) { Geofence geofence = cacheManager.getObject(Geofence.class, geofenceId); if (geofence != null) { @@ -69,7 +62,7 @@ protected Map analyzePosition(Position position) { if (calendar == null || calendar.checkMoment(position.getFixTime())) { Event event = new Event(Event.TYPE_GEOFENCE_EXIT, position); event.setGeofenceId(geofenceId); - events.put(event, position); + callback.eventDetected(event); } } } @@ -79,10 +72,8 @@ protected Map analyzePosition(Position position) { if (calendar == null || calendar.checkMoment(position.getFixTime())) { Event event = new Event(Event.TYPE_GEOFENCE_ENTER, position); event.setGeofenceId(geofenceId); - events.put(event, position); + callback.eventDetected(event); } } - return events; } - } diff --git a/src/main/java/org/traccar/handler/events/IgnitionEventHandler.java b/src/main/java/org/traccar/handler/events/IgnitionEventHandler.java index ba4159a429b..bbf9fadd145 100644 --- a/src/main/java/org/traccar/handler/events/IgnitionEventHandler.java +++ b/src/main/java/org/traccar/handler/events/IgnitionEventHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2024 Anton Tananaev (anton@traccar.org) * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,21 +16,13 @@ */ package org.traccar.handler.events; -import java.util.Collections; -import java.util.Map; - -import io.netty.channel.ChannelHandler; +import jakarta.inject.Inject; import org.traccar.helper.model.PositionUtil; import org.traccar.model.Device; import org.traccar.model.Event; import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import jakarta.inject.Inject; -import jakarta.inject.Singleton; - -@Singleton -@ChannelHandler.Sharable public class IgnitionEventHandler extends BaseEventHandler { private final CacheManager cacheManager; @@ -41,14 +33,12 @@ public IgnitionEventHandler(CacheManager cacheManager) { } @Override - protected Map analyzePosition(Position position) { + public void analyzePosition(Position position, Callback callback) { Device device = cacheManager.getObject(Device.class, position.getDeviceId()); if (device == null || !PositionUtil.isLatest(cacheManager, position)) { - return null; + return; } - Map result = null; - if (position.hasAttribute(Position.KEY_IGNITION)) { boolean ignition = position.getBoolean(Position.KEY_IGNITION); @@ -57,15 +47,12 @@ protected Map analyzePosition(Position position) { boolean oldIgnition = lastPosition.getBoolean(Position.KEY_IGNITION); if (ignition && !oldIgnition) { - result = Collections.singletonMap( - new Event(Event.TYPE_IGNITION_ON, position), position); + callback.eventDetected(new Event(Event.TYPE_IGNITION_ON, position)); } else if (!ignition && oldIgnition) { - result = Collections.singletonMap( - new Event(Event.TYPE_IGNITION_OFF, position), position); + callback.eventDetected(new Event(Event.TYPE_IGNITION_OFF, position)); } } } - return result; } } diff --git a/src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java b/src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java index 2fa2e8869a6..573ad4ad664 100644 --- a/src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java +++ b/src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java @@ -16,20 +16,12 @@ */ package org.traccar.handler.events; -import java.util.HashMap; -import java.util.Map; - -import io.netty.channel.ChannelHandler; +import jakarta.inject.Inject; import org.traccar.model.Event; import org.traccar.model.Maintenance; import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import jakarta.inject.Inject; -import jakarta.inject.Singleton; - -@Singleton -@ChannelHandler.Sharable public class MaintenanceEventHandler extends BaseEventHandler { private final CacheManager cacheManager; @@ -40,13 +32,12 @@ public MaintenanceEventHandler(CacheManager cacheManager) { } @Override - protected Map analyzePosition(Position position) { + public void analyzePosition(Position position, Callback callback) { Position lastPosition = cacheManager.getPosition(position.getDeviceId()); if (lastPosition == null || position.getFixTime().compareTo(lastPosition.getFixTime()) < 0) { - return null; + return; } - Map events = new HashMap<>(); for (Maintenance maintenance : cacheManager.getDeviceObjects(position.getDeviceId(), Maintenance.class)) { if (maintenance.getPeriod() != 0) { double oldValue = getValue(lastPosition, maintenance.getType()); @@ -58,13 +49,11 @@ protected Map analyzePosition(Position position) { Event event = new Event(Event.TYPE_MAINTENANCE, position); event.setMaintenanceId(maintenance.getId()); event.set(maintenance.getType(), newValue); - events.put(event, position); + callback.eventDetected(event); } } } } - - return events; } private double getValue(Position position, String type) { diff --git a/src/main/java/org/traccar/handler/events/MediaEventHandler.java b/src/main/java/org/traccar/handler/events/MediaEventHandler.java index 52d8e6961c7..2745296c431 100644 --- a/src/main/java/org/traccar/handler/events/MediaEventHandler.java +++ b/src/main/java/org/traccar/handler/events/MediaEventHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2022 - 2024 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. @@ -15,18 +15,12 @@ */ package org.traccar.handler.events; -import io.netty.channel.ChannelHandler; +import jakarta.inject.Inject; import org.traccar.model.Event; import org.traccar.model.Position; -import jakarta.inject.Inject; -import jakarta.inject.Singleton; -import java.util.Map; -import java.util.stream.Collectors; import java.util.stream.Stream; -@Singleton -@ChannelHandler.Sharable public class MediaEventHandler extends BaseEventHandler { @Inject @@ -34,8 +28,8 @@ public MediaEventHandler() { } @Override - protected Map analyzePosition(Position position) { - return Stream.of(Position.KEY_IMAGE, Position.KEY_VIDEO, Position.KEY_AUDIO) + public void analyzePosition(Position position, Callback callback) { + Stream.of(Position.KEY_IMAGE, Position.KEY_VIDEO, Position.KEY_AUDIO) .filter(position::hasAttribute) .map(type -> { Event event = new Event(Event.TYPE_MEDIA, position); @@ -43,7 +37,7 @@ protected Map analyzePosition(Position position) { event.set("file", position.getString(type)); return event; }) - .collect(Collectors.toMap(event -> event, event -> position)); + .forEach(callback::eventDetected); } } diff --git a/src/main/java/org/traccar/handler/events/MotionEventHandler.java b/src/main/java/org/traccar/handler/events/MotionEventHandler.java index 15902d6d4a1..41d68985bed 100644 --- a/src/main/java/org/traccar/handler/events/MotionEventHandler.java +++ b/src/main/java/org/traccar/handler/events/MotionEventHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2023 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2024 Anton Tananaev (anton@traccar.org) * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,14 +16,13 @@ */ package org.traccar.handler.events; -import io.netty.channel.ChannelHandler; +import jakarta.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.config.Keys; import org.traccar.helper.model.AttributeUtil; import org.traccar.helper.model.PositionUtil; import org.traccar.model.Device; -import org.traccar.model.Event; import org.traccar.model.Position; import org.traccar.reports.common.TripsConfig; import org.traccar.session.cache.CacheManager; @@ -35,13 +34,6 @@ import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import jakarta.inject.Inject; -import jakarta.inject.Singleton; -import java.util.Collections; -import java.util.Map; - -@Singleton -@ChannelHandler.Sharable public class MotionEventHandler extends BaseEventHandler { private static final Logger LOGGER = LoggerFactory.getLogger(MotionEventHandler.class); @@ -56,17 +48,17 @@ public MotionEventHandler(CacheManager cacheManager, Storage storage) { } @Override - protected Map analyzePosition(Position position) { + public void analyzePosition(Position position, Callback callback) { long deviceId = position.getDeviceId(); Device device = cacheManager.getObject(Device.class, deviceId); if (device == null || !PositionUtil.isLatest(cacheManager, position)) { - return null; + return; } boolean processInvalid = AttributeUtil.lookup( cacheManager, Keys.EVENT_MOTION_PROCESS_INVALID_POSITIONS, deviceId); if (!processInvalid && !position.getValid()) { - return null; + return; } TripsConfig tripsConfig = new TripsConfig(new AttributeUtil.CacheProvider(cacheManager, deviceId)); @@ -82,7 +74,9 @@ protected Map analyzePosition(Position position) { LOGGER.warn("Update device motion error", e); } } - return state.getEvent() != null ? Collections.singletonMap(state.getEvent(), position) : null; + if (state.getEvent() != null) { + callback.eventDetected(state.getEvent()); + } } } diff --git a/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java b/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java index 3bb5f713c24..9598581e6c0 100644 --- a/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java +++ b/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2023 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2024 Anton Tananaev (anton@traccar.org) * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,7 +16,7 @@ */ package org.traccar.handler.events; -import io.netty.channel.ChannelHandler; +import jakarta.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.config.Config; @@ -24,7 +24,6 @@ import org.traccar.helper.model.AttributeUtil; import org.traccar.helper.model.PositionUtil; import org.traccar.model.Device; -import org.traccar.model.Event; import org.traccar.model.Geofence; import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; @@ -36,13 +35,6 @@ import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import jakarta.inject.Inject; -import jakarta.inject.Singleton; -import java.util.Collections; -import java.util.Map; - -@Singleton -@ChannelHandler.Sharable public class OverspeedEventHandler extends BaseEventHandler { private static final Logger LOGGER = LoggerFactory.getLogger(OverspeedEventHandler.class); @@ -55,8 +47,7 @@ public class OverspeedEventHandler extends BaseEventHandler { private final double multiplier; @Inject - public OverspeedEventHandler( - Config config, CacheManager cacheManager, Storage storage) { + public OverspeedEventHandler(Config config, CacheManager cacheManager, Storage storage) { this.cacheManager = cacheManager; this.storage = storage; minimalDuration = config.getLong(Keys.EVENT_OVERSPEED_MINIMAL_DURATION) * 1000; @@ -65,15 +56,15 @@ public OverspeedEventHandler( } @Override - protected Map analyzePosition(Position position) { + public void analyzePosition(Position position, Callback callback) { long deviceId = position.getDeviceId(); Device device = cacheManager.getObject(Device.class, position.getDeviceId()); if (device == null) { - return null; + return; } if (!PositionUtil.isLatest(cacheManager, position) || !position.getValid()) { - return null; + return; } double speedLimit = AttributeUtil.lookup(cacheManager, Keys.EVENT_OVERSPEED_LIMIT, deviceId); @@ -105,7 +96,7 @@ protected Map analyzePosition(Position position) { } if (speedLimit == 0) { - return null; + return; } OverspeedState state = OverspeedState.fromDevice(device); @@ -120,7 +111,9 @@ protected Map analyzePosition(Position position) { LOGGER.warn("Update device overspeed error", e); } } - return state.getEvent() != null ? Collections.singletonMap(state.getEvent(), position) : null; + if (state.getEvent() != null) { + callback.eventDetected(state.getEvent()); + } } } diff --git a/src/main/java/org/traccar/handler/AcknowledgementHandler.java b/src/main/java/org/traccar/handler/network/AcknowledgementHandler.java similarity index 99% rename from src/main/java/org/traccar/handler/AcknowledgementHandler.java rename to src/main/java/org/traccar/handler/network/AcknowledgementHandler.java index 4c10859984f..e87f5d34c46 100644 --- a/src/main/java/org/traccar/handler/AcknowledgementHandler.java +++ b/src/main/java/org/traccar/handler/network/AcknowledgementHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.traccar.handler; +package org.traccar.handler.network; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelOutboundHandlerAdapter; diff --git a/src/main/java/org/traccar/MainEventHandler.java b/src/main/java/org/traccar/handler/network/MainEventHandler.java similarity index 98% rename from src/main/java/org/traccar/MainEventHandler.java rename to src/main/java/org/traccar/handler/network/MainEventHandler.java index fb0171d63d0..901036d4476 100644 --- a/src/main/java/org/traccar/MainEventHandler.java +++ b/src/main/java/org/traccar/handler/network/MainEventHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.traccar; +package org.traccar.handler.network; import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; @@ -24,10 +24,11 @@ import io.netty.handler.timeout.IdleStateEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.traccar.BasePipelineFactory; +import org.traccar.BaseProtocolDecoder; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.database.StatisticsManager; -import org.traccar.handler.AcknowledgementHandler; import org.traccar.helper.DateUtil; import org.traccar.helper.NetworkUtil; import org.traccar.helper.model.PositionUtil; diff --git a/src/main/java/org/traccar/handler/NetworkForwarderHandler.java b/src/main/java/org/traccar/handler/network/NetworkForwarderHandler.java similarity index 98% rename from src/main/java/org/traccar/handler/NetworkForwarderHandler.java rename to src/main/java/org/traccar/handler/network/NetworkForwarderHandler.java index 470e175ca6b..5f07ce35599 100644 --- a/src/main/java/org/traccar/handler/NetworkForwarderHandler.java +++ b/src/main/java/org/traccar/handler/network/NetworkForwarderHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.traccar.handler; +package org.traccar.handler.network; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; diff --git a/src/main/java/org/traccar/handler/NetworkMessageHandler.java b/src/main/java/org/traccar/handler/network/NetworkMessageHandler.java similarity index 98% rename from src/main/java/org/traccar/handler/NetworkMessageHandler.java rename to src/main/java/org/traccar/handler/network/NetworkMessageHandler.java index b1d926bfa57..c2fb9016ae1 100644 --- a/src/main/java/org/traccar/handler/NetworkMessageHandler.java +++ b/src/main/java/org/traccar/handler/network/NetworkMessageHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.traccar.handler; +package org.traccar.handler.network; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelDuplexHandler; diff --git a/src/main/java/org/traccar/handler/OpenChannelHandler.java b/src/main/java/org/traccar/handler/network/OpenChannelHandler.java similarity index 97% rename from src/main/java/org/traccar/handler/OpenChannelHandler.java rename to src/main/java/org/traccar/handler/network/OpenChannelHandler.java index e416f35ae75..21aaae67689 100644 --- a/src/main/java/org/traccar/handler/OpenChannelHandler.java +++ b/src/main/java/org/traccar/handler/network/OpenChannelHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.traccar.handler; +package org.traccar.handler.network; import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandlerContext; diff --git a/src/main/java/org/traccar/handler/RemoteAddressHandler.java b/src/main/java/org/traccar/handler/network/RemoteAddressHandler.java similarity index 92% rename from src/main/java/org/traccar/handler/RemoteAddressHandler.java rename to src/main/java/org/traccar/handler/network/RemoteAddressHandler.java index 61ada5b9113..d545ed799e9 100644 --- a/src/main/java/org/traccar/handler/RemoteAddressHandler.java +++ b/src/main/java/org/traccar/handler/network/RemoteAddressHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2024 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. @@ -13,20 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.traccar.handler; +package org.traccar.handler.network; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; +import jakarta.inject.Inject; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.model.Position; -import jakarta.inject.Inject; -import jakarta.inject.Singleton; import java.net.InetSocketAddress; -@Singleton @ChannelHandler.Sharable public class RemoteAddressHandler extends ChannelInboundHandlerAdapter { diff --git a/src/main/java/org/traccar/handler/StandardLoggingHandler.java b/src/main/java/org/traccar/handler/network/StandardLoggingHandler.java similarity index 98% rename from src/main/java/org/traccar/handler/StandardLoggingHandler.java rename to src/main/java/org/traccar/handler/network/StandardLoggingHandler.java index 513602dd8a9..dae93655da4 100644 --- a/src/main/java/org/traccar/handler/StandardLoggingHandler.java +++ b/src/main/java/org/traccar/handler/network/StandardLoggingHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.traccar.handler; +package org.traccar.handler.network; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; diff --git a/src/test/java/org/traccar/handler/DistanceHandlerTest.java b/src/test/java/org/traccar/handler/DistanceHandlerTest.java index 7d2f1e2e3a6..04a27ad7986 100644 --- a/src/test/java/org/traccar/handler/DistanceHandlerTest.java +++ b/src/test/java/org/traccar/handler/DistanceHandlerTest.java @@ -15,14 +15,15 @@ public void testCalculateDistance() { DistanceHandler distanceHandler = new DistanceHandler(new Config(), mock(CacheManager.class)); - Position position = distanceHandler.handlePosition(new Position()); + Position position = new Position(); + distanceHandler.handlePosition(position, p -> {}); assertEquals(0.0, position.getAttributes().get(Position.KEY_DISTANCE)); assertEquals(0.0, position.getAttributes().get(Position.KEY_TOTAL_DISTANCE)); position.set(Position.KEY_DISTANCE, 100); - position = distanceHandler.handlePosition(position); + distanceHandler.handlePosition(position, p -> {}); assertEquals(100.0, position.getAttributes().get(Position.KEY_DISTANCE)); assertEquals(100.0, position.getAttributes().get(Position.KEY_TOTAL_DISTANCE)); diff --git a/src/test/java/org/traccar/handler/MotionHandlerTest.java b/src/test/java/org/traccar/handler/MotionHandlerTest.java index 10cdf6a9020..927c803d9bb 100644 --- a/src/test/java/org/traccar/handler/MotionHandlerTest.java +++ b/src/test/java/org/traccar/handler/MotionHandlerTest.java @@ -26,7 +26,8 @@ public void testCalculateMotion() { MotionHandler motionHandler = new MotionHandler(cacheManager); - Position position = motionHandler.handlePosition(new Position()); + Position position = new Position(); + motionHandler.handlePosition(position, p -> {}); assertEquals(false, position.getAttributes().get(Position.KEY_MOTION)); diff --git a/src/test/java/org/traccar/handler/events/AlertEventHandlerTest.java b/src/test/java/org/traccar/handler/events/AlertEventHandlerTest.java index 66dc55c85c7..7b1991553e5 100644 --- a/src/test/java/org/traccar/handler/events/AlertEventHandlerTest.java +++ b/src/test/java/org/traccar/handler/events/AlertEventHandlerTest.java @@ -7,10 +7,11 @@ import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import java.util.Map; +import java.util.ArrayList; +import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.mockito.Mockito.mock; public class AlertEventHandlerTest extends BaseTest { @@ -22,9 +23,10 @@ public void testAlertEventHandler() { Position position = new Position(); position.set(Position.KEY_ALARM, Position.ALARM_GENERAL); - Map events = alertEventHandler.analyzePosition(position); - assertNotNull(events); - Event event = events.keySet().iterator().next(); + List events = new ArrayList<>(); + alertEventHandler.analyzePosition(position, events::add); + assertFalse(events.isEmpty()); + Event event = events.iterator().next(); assertEquals(Event.TYPE_ALARM, event.getType()); } diff --git a/src/test/java/org/traccar/handler/events/CommandResultEventHandlerTest.java b/src/test/java/org/traccar/handler/events/CommandResultEventHandlerTest.java index bc24e42f56b..58e198b7f32 100644 --- a/src/test/java/org/traccar/handler/events/CommandResultEventHandlerTest.java +++ b/src/test/java/org/traccar/handler/events/CommandResultEventHandlerTest.java @@ -5,9 +5,12 @@ import org.traccar.model.Event; import org.traccar.model.Position; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; public class CommandResultEventHandlerTest extends BaseTest { @@ -19,9 +22,10 @@ public void testCommandResultEventHandler() throws Exception { Position position = new Position(); position.set(Position.KEY_RESULT, "Test Result"); - Map events = commandResultEventHandler.analyzePosition(position); - assertNotNull(events); - Event event = events.keySet().iterator().next(); + List events = new ArrayList<>(); + commandResultEventHandler.analyzePosition(position, events::add); + assertFalse(events.isEmpty()); + Event event = events.iterator().next(); assertEquals(Event.TYPE_COMMAND_RESULT, event.getType()); } diff --git a/src/test/java/org/traccar/handler/events/IgnitionEventHandlerTest.java b/src/test/java/org/traccar/handler/events/IgnitionEventHandlerTest.java index 972932df4d7..61ff50efa63 100644 --- a/src/test/java/org/traccar/handler/events/IgnitionEventHandlerTest.java +++ b/src/test/java/org/traccar/handler/events/IgnitionEventHandlerTest.java @@ -1,14 +1,11 @@ package org.traccar.handler.events; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.traccar.BaseTest; -import org.traccar.model.Event; import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.Mockito.mock; public class IgnitionEventHandlerTest extends BaseTest { @@ -21,8 +18,7 @@ public void testIgnitionEventHandler() { Position position = new Position(); position.set(Position.KEY_IGNITION, true); position.setValid(true); - Map events = ignitionEventHandler.analyzePosition(position); - assertNull(events); + ignitionEventHandler.analyzePosition(position, Assertions::assertNull); } } diff --git a/src/test/java/org/traccar/handler/events/MaintenanceEventHandlerTest.java b/src/test/java/org/traccar/handler/events/MaintenanceEventHandlerTest.java index 661336d766f..f0bd4a3f98f 100644 --- a/src/test/java/org/traccar/handler/events/MaintenanceEventHandlerTest.java +++ b/src/test/java/org/traccar/handler/events/MaintenanceEventHandlerTest.java @@ -2,11 +2,14 @@ import org.junit.jupiter.api.Test; import org.traccar.BaseTest; +import org.traccar.model.Event; import org.traccar.model.Maintenance; import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; +import java.util.ArrayList; import java.util.Date; +import java.util.List; import java.util.Set; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -38,24 +41,30 @@ public void testMaintenanceEventHandler() { MaintenanceEventHandler eventHandler = new MaintenanceEventHandler(cacheManager); when(maintenance.getStart()).thenReturn(10000.0); - when(maintenance.getPeriod()).thenReturn(2000.0); + when(maintenance.getPeriod()).thenReturn(2000.0); + + List events = new ArrayList<>(); lastPosition.set(Position.KEY_TOTAL_DISTANCE, 1999); - position.set(Position.KEY_TOTAL_DISTANCE, 2001); - assertTrue(eventHandler.analyzePosition(position).isEmpty()); + position.set(Position.KEY_TOTAL_DISTANCE, 2001); + eventHandler.analyzePosition(position, events::add); + assertTrue(events.isEmpty()); lastPosition.set(Position.KEY_TOTAL_DISTANCE, 3999); - position.set(Position.KEY_TOTAL_DISTANCE, 4001); - assertTrue(eventHandler.analyzePosition(position).isEmpty()); + position.set(Position.KEY_TOTAL_DISTANCE, 4001); + eventHandler.analyzePosition(position, events::add); + assertTrue(events.isEmpty()); lastPosition.set(Position.KEY_TOTAL_DISTANCE, 9999); position.set(Position.KEY_TOTAL_DISTANCE, 10001); - assertEquals(1, eventHandler.analyzePosition(position).size()); + eventHandler.analyzePosition(position, events::add); + assertEquals(1, events.size()); lastPosition.set(Position.KEY_TOTAL_DISTANCE, 11999); position.set(Position.KEY_TOTAL_DISTANCE, 12001); - assertEquals(1, eventHandler.analyzePosition(position).size()); - + eventHandler.analyzePosition(position, events::add); + assertEquals(2, events.size()); + } } From 0f7ec1247cff1a68ffa57ade3f45b11f6602e262 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 30 Mar 2024 19:36:35 -0700 Subject: [PATCH 33/42] Make handlers nullable --- src/main/java/org/traccar/MainModule.java | 31 +++++++++++++++++++ .../handler/CopyAttributesHandler.java | 18 +++++------ .../org/traccar/handler/FilterHandler.java | 4 +-- .../java/org/traccar/handler/TimeHandler.java | 22 +++++-------- 4 files changed, 46 insertions(+), 29 deletions(-) diff --git a/src/main/java/org/traccar/MainModule.java b/src/main/java/org/traccar/MainModule.java index 26654947e8c..791d61c61e7 100644 --- a/src/main/java/org/traccar/MainModule.java +++ b/src/main/java/org/traccar/MainModule.java @@ -72,9 +72,12 @@ import org.traccar.geolocation.GoogleGeolocationProvider; import org.traccar.geolocation.OpenCellIdGeolocationProvider; import org.traccar.geolocation.UnwiredGeolocationProvider; +import org.traccar.handler.CopyAttributesHandler; +import org.traccar.handler.FilterHandler; import org.traccar.handler.GeocoderHandler; import org.traccar.handler.GeolocationHandler; import org.traccar.handler.SpeedLimitHandler; +import org.traccar.handler.TimeHandler; import org.traccar.helper.ObjectMapperContextResolver; import org.traccar.helper.SanitizerModule; import org.traccar.helper.WebHelper; @@ -336,6 +339,34 @@ public static SpeedLimitHandler provideSpeedLimitHandler(@Nullable SpeedLimitPro return null; } + @Singleton + @Provides + public static CopyAttributesHandler provideCopyAttributesHandler(Config config, CacheManager cacheManager) { + if (config.getBoolean(Keys.PROCESSING_COPY_ATTRIBUTES_ENABLE)) { + return new CopyAttributesHandler(config, cacheManager); + } + return null; + } + + @Singleton + @Provides + public static FilterHandler provideFilterHandler( + Config config, CacheManager cacheManager, Storage storage, StatisticsManager statisticsManager) { + if (config.getBoolean(Keys.FILTER_ENABLE)) { + return new FilterHandler(config, cacheManager, storage, statisticsManager); + } + return null; + } + + @Singleton + @Provides + public static TimeHandler provideTimeHandler(Config config) { + if (config.hasKey(Keys.TIME_OVERRIDE)) { + return new TimeHandler(config); + } + return null; + } + @Singleton @Provides public static BroadcastService provideBroadcastService( diff --git a/src/main/java/org/traccar/handler/CopyAttributesHandler.java b/src/main/java/org/traccar/handler/CopyAttributesHandler.java index 9c31bf56e16..f682c99c9d4 100644 --- a/src/main/java/org/traccar/handler/CopyAttributesHandler.java +++ b/src/main/java/org/traccar/handler/CopyAttributesHandler.java @@ -25,26 +25,22 @@ public class CopyAttributesHandler extends BasePositionHandler { - private final boolean enabled; private final CacheManager cacheManager; @Inject public CopyAttributesHandler(Config config, CacheManager cacheManager) { - enabled = config.getBoolean(Keys.PROCESSING_COPY_ATTRIBUTES_ENABLE); this.cacheManager = cacheManager; } @Override public void handlePosition(Position position, Callback callback) { - if (enabled) { - String attributesString = AttributeUtil.lookup( - cacheManager, Keys.PROCESSING_COPY_ATTRIBUTES, position.getDeviceId()); - Position last = cacheManager.getPosition(position.getDeviceId()); - if (last != null && attributesString != null) { - for (String attribute : attributesString.split("[ ,]")) { - if (last.hasAttribute(attribute) && !position.hasAttribute(attribute)) { - position.getAttributes().put(attribute, last.getAttributes().get(attribute)); - } + String attributesString = AttributeUtil.lookup( + cacheManager, Keys.PROCESSING_COPY_ATTRIBUTES, position.getDeviceId()); + Position last = cacheManager.getPosition(position.getDeviceId()); + if (last != null && attributesString != null) { + for (String attribute : attributesString.split("[ ,]")) { + if (last.hasAttribute(attribute) && !position.hasAttribute(attribute)) { + position.getAttributes().put(attribute, last.getAttributes().get(attribute)); } } } diff --git a/src/main/java/org/traccar/handler/FilterHandler.java b/src/main/java/org/traccar/handler/FilterHandler.java index 4cc6233d0f4..a9e6024c893 100644 --- a/src/main/java/org/traccar/handler/FilterHandler.java +++ b/src/main/java/org/traccar/handler/FilterHandler.java @@ -40,7 +40,6 @@ public class FilterHandler extends BasePositionHandler { private static final Logger LOGGER = LoggerFactory.getLogger(FilterHandler.class); - private final boolean enabled; private final boolean filterInvalid; private final boolean filterZero; private final boolean filterDuplicate; @@ -65,7 +64,6 @@ public class FilterHandler extends BasePositionHandler { @Inject public FilterHandler( Config config, CacheManager cacheManager, Storage storage, StatisticsManager statisticsManager) { - enabled = config.getBoolean(Keys.FILTER_ENABLE); filterInvalid = config.getBoolean(Keys.FILTER_INVALID); filterZero = config.getBoolean(Keys.FILTER_ZERO); filterDuplicate = config.getBoolean(Keys.FILTER_DUPLICATE); @@ -272,7 +270,7 @@ protected boolean filter(Position position) { @Override public void handlePosition(Position position, Callback callback) { - if (enabled && filter(position)) { + if (filter(position)) { callback.processed(null); } else { callback.processed(position); diff --git a/src/main/java/org/traccar/handler/TimeHandler.java b/src/main/java/org/traccar/handler/TimeHandler.java index 2e703c681b7..052ad41c3f6 100644 --- a/src/main/java/org/traccar/handler/TimeHandler.java +++ b/src/main/java/org/traccar/handler/TimeHandler.java @@ -26,18 +26,12 @@ public class TimeHandler extends BasePositionHandler { - private final boolean enabled; private final boolean useServerTime; private final Set protocols; @Inject public TimeHandler(Config config) { - enabled = config.hasKey(Keys.TIME_OVERRIDE); - if (enabled) { - useServerTime = config.getString(Keys.TIME_OVERRIDE).equalsIgnoreCase("serverTime"); - } else { - useServerTime = false; - } + useServerTime = config.getString(Keys.TIME_OVERRIDE).equalsIgnoreCase("serverTime"); String protocolList = config.getString(Keys.TIME_PROTOCOLS); if (protocolList != null) { protocols = new HashSet<>(Arrays.asList(protocolList.split("[, ]"))); @@ -49,14 +43,12 @@ public TimeHandler(Config config) { @Override public void handlePosition(Position position, Callback callback) { - if (enabled) { - if (protocols == null || protocols.contains(position.getProtocol())) { - if (useServerTime) { - position.setDeviceTime(position.getServerTime()); - position.setFixTime(position.getServerTime()); - } else { - position.setFixTime(position.getDeviceTime()); - } + if (protocols == null || protocols.contains(position.getProtocol())) { + if (useServerTime) { + position.setDeviceTime(position.getServerTime()); + position.setFixTime(position.getServerTime()); + } else { + position.setFixTime(position.getDeviceTime()); } } callback.processed(position); From e7205fa3d1aa6d223cfabfabb32f027415956430 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 30 Mar 2024 19:38:06 -0700 Subject: [PATCH 34/42] Remove unused global timer --- src/main/java/org/traccar/GlobalTimer.java | 42 -------------------- src/main/java/org/traccar/ServerManager.java | 8 +--- 2 files changed, 2 insertions(+), 48 deletions(-) delete mode 100644 src/main/java/org/traccar/GlobalTimer.java diff --git a/src/main/java/org/traccar/GlobalTimer.java b/src/main/java/org/traccar/GlobalTimer.java deleted file mode 100644 index a97321ba216..00000000000 --- a/src/main/java/org/traccar/GlobalTimer.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2012 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; - -import io.netty.util.HashedWheelTimer; -import io.netty.util.Timer; - -public final class GlobalTimer { - - private static Timer instance = null; - - private GlobalTimer() { - } - - public static void release() { - if (instance != null) { - instance.stop(); - } - instance = null; - } - - public static Timer getTimer() { - if (instance == null) { - instance = new HashedWheelTimer(); - } - return instance; - } - -} diff --git a/src/main/java/org/traccar/ServerManager.java b/src/main/java/org/traccar/ServerManager.java index e91be50a25d..22af66b416f 100644 --- a/src/main/java/org/traccar/ServerManager.java +++ b/src/main/java/org/traccar/ServerManager.java @@ -82,12 +82,8 @@ public void start() throws Exception { @Override public void stop() throws Exception { - try { - for (TrackerConnector connector : connectorList) { - connector.stop(); - } - } finally { - GlobalTimer.release(); + for (TrackerConnector connector : connectorList) { + connector.stop(); } } From 2d366f0ab3d88c635bec0f1f498396cd47557f1d Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 30 Mar 2024 19:41:51 -0700 Subject: [PATCH 35/42] Fix remote address handler --- src/main/java/org/traccar/BasePipelineFactory.java | 2 +- .../java/org/traccar/handler/network/RemoteAddressHandler.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/traccar/BasePipelineFactory.java b/src/main/java/org/traccar/BasePipelineFactory.java index 6d9431ad629..40360cca78a 100644 --- a/src/main/java/org/traccar/BasePipelineFactory.java +++ b/src/main/java/org/traccar/BasePipelineFactory.java @@ -96,7 +96,6 @@ protected void initChannel(Channel channel) { pipeline.addLast(injectMembers(new NetworkForwarderHandler(port))); } pipeline.addLast(new NetworkMessageHandler()); - pipeline.addLast(new RemoteAddressHandler(config)); pipeline.addLast(injectMembers(new StandardLoggingHandler(protocol))); if (!connector.isDatagram() && !config.getBoolean(Keys.SERVER_INSTANT_ACKNOWLEDGEMENT)) { @@ -116,6 +115,7 @@ protected void initChannel(Channel channel) { pipeline.addLast(handler); }); + pipeline.addLast(injector.getInstance(RemoteAddressHandler.class)); pipeline.addLast(injector.getInstance(ProcessingHandler.class)); pipeline.addLast(injector.getInstance(MainEventHandler.class)); } diff --git a/src/main/java/org/traccar/handler/network/RemoteAddressHandler.java b/src/main/java/org/traccar/handler/network/RemoteAddressHandler.java index d545ed799e9..c52bb2be189 100644 --- a/src/main/java/org/traccar/handler/network/RemoteAddressHandler.java +++ b/src/main/java/org/traccar/handler/network/RemoteAddressHandler.java @@ -19,12 +19,14 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import jakarta.inject.Inject; +import jakarta.inject.Singleton; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.model.Position; import java.net.InetSocketAddress; +@Singleton @ChannelHandler.Sharable public class RemoteAddressHandler extends ChannelInboundHandlerAdapter { From 05283e44dea22f7b928401f888289840581b28af Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sun, 31 Mar 2024 07:14:34 -0700 Subject: [PATCH 36/42] Simplify main event handler --- .../java/org/traccar/ProcessingHandler.java | 12 +++- .../org/traccar/handler/DatabaseHandler.java | 6 +- .../traccar/handler/PostProcessHandler.java | 67 +++++++++++++++++++ .../handler/network/MainEventHandler.java | 40 ++--------- 4 files changed, 86 insertions(+), 39 deletions(-) create mode 100644 src/main/java/org/traccar/handler/PostProcessHandler.java diff --git a/src/main/java/org/traccar/ProcessingHandler.java b/src/main/java/org/traccar/ProcessingHandler.java index 7627c719b19..09ec79f9d95 100644 --- a/src/main/java/org/traccar/ProcessingHandler.java +++ b/src/main/java/org/traccar/ProcessingHandler.java @@ -26,6 +26,7 @@ import org.traccar.handler.ComputedAttributesHandler; import org.traccar.handler.CopyAttributesHandler; import org.traccar.handler.DatabaseHandler; +import org.traccar.handler.PostProcessHandler; import org.traccar.handler.DistanceHandler; import org.traccar.handler.EngineHoursHandler; import org.traccar.handler.FilterHandler; @@ -65,6 +66,7 @@ public class ProcessingHandler extends ChannelInboundHandlerAdapter { private final NotificationManager notificationManager; private final List positionHandlers; private final List eventHandlers; + private final PostProcessHandler postProcessHandler; @Inject public ProcessingHandler(Injector injector, NotificationManager notificationManager) { @@ -104,14 +106,17 @@ public ProcessingHandler(Injector injector, NotificationManager notificationMana .map((clazz) -> (BaseEventHandler) injector.getInstance(clazz)) .filter(Objects::nonNull) .collect(Collectors.toUnmodifiableList()); + + postProcessHandler = injector.getInstance(PostProcessHandler.class); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof Position) { processPositionHandlers(ctx, (Position) msg); + } else { + super.channelRead(ctx, msg); } - super.channelRead(ctx, msg); } private void processPositionHandlers(ChannelHandlerContext ctx, Position position) { @@ -139,7 +144,10 @@ private void processEventHandlers(ChannelHandlerContext ctx, Position position) } private void finishedProcessing(ChannelHandlerContext ctx, Position position) { - ctx.writeAndFlush(new AcknowledgementHandler.EventHandled(position)); + postProcessHandler.handlePosition(position, p -> { + ctx.fireChannelRead(p); + ctx.writeAndFlush(new AcknowledgementHandler.EventHandled(p)); + }); } } diff --git a/src/main/java/org/traccar/handler/DatabaseHandler.java b/src/main/java/org/traccar/handler/DatabaseHandler.java index b1f218a1e19..0c8d2717d0c 100644 --- a/src/main/java/org/traccar/handler/DatabaseHandler.java +++ b/src/main/java/org/traccar/handler/DatabaseHandler.java @@ -18,6 +18,7 @@ import jakarta.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.traccar.database.StatisticsManager; import org.traccar.model.Position; import org.traccar.storage.Storage; import org.traccar.storage.query.Columns; @@ -28,10 +29,12 @@ public class DatabaseHandler extends BasePositionHandler { private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseHandler.class); private final Storage storage; + private final StatisticsManager statisticsManager; @Inject - public DatabaseHandler(Storage storage) { + public DatabaseHandler(Storage storage, StatisticsManager statisticsManager) { this.storage = storage; + this.statisticsManager = statisticsManager; } @Override @@ -39,6 +42,7 @@ public void handlePosition(Position position, Callback callback) { try { position.setId(storage.addObject(position, new Request(new Columns.Exclude("id")))); + statisticsManager.messageStoredCount(position.getDeviceId()); } catch (Exception error) { LOGGER.warn("Failed to store position", error); } diff --git a/src/main/java/org/traccar/handler/PostProcessHandler.java b/src/main/java/org/traccar/handler/PostProcessHandler.java new file mode 100644 index 00000000000..2d6bc03ad11 --- /dev/null +++ b/src/main/java/org/traccar/handler/PostProcessHandler.java @@ -0,0 +1,67 @@ +/* + * Copyright 2024 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.handler; + +import jakarta.inject.Inject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.helper.model.PositionUtil; +import org.traccar.model.Device; +import org.traccar.model.Position; +import org.traccar.session.ConnectionManager; +import org.traccar.session.cache.CacheManager; +import org.traccar.storage.Storage; +import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Request; + +public class PostProcessHandler extends BasePositionHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(PostProcessHandler.class); + + private final CacheManager cacheManager; + private final Storage storage; + private final ConnectionManager connectionManager; + + @Inject + public PostProcessHandler(CacheManager cacheManager, Storage storage, ConnectionManager connectionManager) { + this.cacheManager = cacheManager; + this.storage = storage; + this.connectionManager = connectionManager; + } + + @Override + public void handlePosition(Position position, Callback callback) { + try { + if (PositionUtil.isLatest(cacheManager, position)) { + Device updatedDevice = new Device(); + updatedDevice.setId(position.getDeviceId()); + updatedDevice.setPositionId(position.getId()); + storage.updateObject(updatedDevice, new Request( + new Columns.Include("positionId"), + new Condition.Equals("id", updatedDevice.getId()))); + + cacheManager.updatePosition(position); + connectionManager.updatePosition(true, position); + } + } catch (StorageException error) { + LOGGER.warn("Failed to update device", error); + } + callback.processed(position); + } + +} diff --git a/src/main/java/org/traccar/handler/network/MainEventHandler.java b/src/main/java/org/traccar/handler/network/MainEventHandler.java index 901036d4476..d6407f1e5cd 100644 --- a/src/main/java/org/traccar/handler/network/MainEventHandler.java +++ b/src/main/java/org/traccar/handler/network/MainEventHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2024 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,28 +22,21 @@ import io.netty.channel.socket.DatagramChannel; import io.netty.handler.codec.http.HttpRequestDecoder; import io.netty.handler.timeout.IdleStateEvent; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.BasePipelineFactory; import org.traccar.BaseProtocolDecoder; import org.traccar.config.Config; import org.traccar.config.Keys; -import org.traccar.database.StatisticsManager; import org.traccar.helper.DateUtil; import org.traccar.helper.NetworkUtil; -import org.traccar.helper.model.PositionUtil; import org.traccar.model.Device; import org.traccar.model.Position; import org.traccar.session.ConnectionManager; import org.traccar.session.cache.CacheManager; -import org.traccar.storage.Storage; -import org.traccar.storage.StorageException; -import org.traccar.storage.query.Columns; -import org.traccar.storage.query.Condition; -import org.traccar.storage.query.Request; -import jakarta.inject.Inject; -import jakarta.inject.Singleton; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedHashSet; @@ -59,18 +52,13 @@ public class MainEventHandler extends ChannelInboundHandlerAdapter { private final Set logAttributes = new LinkedHashSet<>(); private final CacheManager cacheManager; - private final Storage storage; private final ConnectionManager connectionManager; - private final StatisticsManager statisticsManager; @Inject public MainEventHandler( - Config config, CacheManager cacheManager, Storage storage, ConnectionManager connectionManager, - StatisticsManager statisticsManager) { + Config config, CacheManager cacheManager, ConnectionManager connectionManager) { this.cacheManager = cacheManager; - this.storage = storage; this.connectionManager = connectionManager; - this.statisticsManager = statisticsManager; String connectionlessProtocolList = config.getString(Keys.STATUS_IGNORE_OFFLINE); if (connectionlessProtocolList != null) { connectionlessProtocols.addAll(Arrays.asList(connectionlessProtocolList.split("[, ]"))); @@ -85,22 +73,6 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { Position position = (Position) msg; Device device = cacheManager.getObject(Device.class, position.getDeviceId()); - try { - if (PositionUtil.isLatest(cacheManager, position)) { - Device updatedDevice = new Device(); - updatedDevice.setId(position.getDeviceId()); - updatedDevice.setPositionId(position.getId()); - storage.updateObject(updatedDevice, new Request( - new Columns.Include("positionId"), - new Condition.Equals("id", updatedDevice.getId()))); - - cacheManager.updatePosition(position); - connectionManager.updatePosition(true, position); - } - } catch (StorageException error) { - LOGGER.warn("Failed to update device", error); - } - StringBuilder builder = new StringBuilder(); builder.append("[").append(NetworkUtil.session(ctx.channel())).append("] "); builder.append("id: ").append(device.getUniqueId()); @@ -145,10 +117,6 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { } } LOGGER.info(builder.toString()); - - statisticsManager.registerMessageStored(position.getDeviceId(), position.getProtocol()); - - ctx.writeAndFlush(new AcknowledgementHandler.EventHandled(position)); } } From 0b403da4f4fe83517110a09acb1d280396379dfb Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sun, 31 Mar 2024 08:07:22 -0700 Subject: [PATCH 37/42] Delete unused group tree --- .../java/org/traccar/database/GroupTree.java | 151 ------------------ .../org/traccar/database/GroupTreeTest.java | 56 ------- 2 files changed, 207 deletions(-) delete mode 100644 src/main/java/org/traccar/database/GroupTree.java delete mode 100644 src/test/java/org/traccar/database/GroupTreeTest.java diff --git a/src/main/java/org/traccar/database/GroupTree.java b/src/main/java/org/traccar/database/GroupTree.java deleted file mode 100644 index 8798f55bcf8..00000000000 --- a/src/main/java/org/traccar/database/GroupTree.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright 2016 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.database; - -import org.traccar.model.Device; -import org.traccar.model.Group; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -public class GroupTree { - - private static class TreeNode { - - private Group group; - private Device device; - private Collection children = new HashSet<>(); - - TreeNode(Group group) { - this.group = group; - } - - TreeNode(Device device) { - this.device = device; - } - - @Override - public int hashCode() { - if (group != null) { - return (int) group.getId(); - } else { - return (int) device.getId(); - } - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof TreeNode)) { - return false; - } - TreeNode other = (TreeNode) obj; - if (other == this) { - return true; - } - if (group != null && other.group != null) { - return group.getId() == other.group.getId(); - } else if (device != null && other.device != null) { - return device.getId() == other.device.getId(); - } - return false; - } - - public Group getGroup() { - return group; - } - - public Device getDevice() { - return device; - } - - public void setParent(TreeNode parent) { - if (parent != null) { - parent.children.add(this); - } - } - - public Collection getChildren() { - return children; - } - - } - - private final Map groupMap = new HashMap<>(); - - public GroupTree(Collection groups, Collection devices) { - - for (Group group : groups) { - groupMap.put(group.getId(), new TreeNode(group)); - } - - for (TreeNode node : groupMap.values()) { - if (node.getGroup().getGroupId() != 0) { - node.setParent(groupMap.get(node.getGroup().getGroupId())); - } - } - - Map deviceMap = new HashMap<>(); - - for (Device device : devices) { - deviceMap.put(device.getId(), new TreeNode(device)); - } - - for (TreeNode node : deviceMap.values()) { - if (node.getDevice().getGroupId() != 0) { - node.setParent(groupMap.get(node.getDevice().getGroupId())); - } - } - - } - - public Collection getGroups(long groupId) { - Set results = new HashSet<>(); - getNodes(results, groupMap.get(groupId)); - Collection groups = new ArrayList<>(); - for (TreeNode node : results) { - if (node.getGroup() != null) { - groups.add(node.getGroup()); - } - } - return groups; - } - - public Collection getDevices(long groupId) { - Set results = new HashSet<>(); - getNodes(results, groupMap.get(groupId)); - Collection devices = new ArrayList<>(); - for (TreeNode node : results) { - if (node.getDevice() != null) { - devices.add(node.getDevice()); - } - } - return devices; - } - - private void getNodes(Set results, TreeNode node) { - if (node != null) { - for (TreeNode child : node.getChildren()) { - results.add(child); - getNodes(results, child); - } - } - } - -} diff --git a/src/test/java/org/traccar/database/GroupTreeTest.java b/src/test/java/org/traccar/database/GroupTreeTest.java deleted file mode 100644 index 7c0d478f8ff..00000000000 --- a/src/test/java/org/traccar/database/GroupTreeTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.traccar.database; - -import org.junit.jupiter.api.Test; -import org.traccar.model.Device; -import org.traccar.model.Group; - -import java.util.ArrayList; -import java.util.Collection; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class GroupTreeTest { - - private static Group createGroup(long id, String name, long parent) { - Group group = new Group(); - group.setId(id); - group.setName(name); - group.setGroupId(parent); - return group; - } - - private static Device createDevice(long id, String name, long parent) { - Device device = new Device(); - device.setId(id); - device.setName(name); - device.setGroupId(parent); - return device; - } - - @Test - public void testGetDescendants() { - Collection groups = new ArrayList<>(); - groups.add(createGroup(1, "First", 0)); - groups.add(createGroup(2, "Second", 1)); - groups.add(createGroup(3, "Third", 2)); - groups.add(createGroup(4, "Fourth", 2)); - groups.add(createGroup(5, "Fifth", 4)); - - Collection devices = new ArrayList<>(); - devices.add(createDevice(1, "One", 3)); - devices.add(createDevice(2, "Two", 5)); - devices.add(createDevice(3, "One", 5)); - - GroupTree groupTree = new GroupTree(groups, devices); - - assertEquals(4, groupTree.getGroups(1).size()); - assertEquals(3, groupTree.getGroups(2).size()); - assertEquals(0, groupTree.getGroups(3).size()); - assertEquals(1, groupTree.getGroups(4).size()); - - assertEquals(3, groupTree.getDevices(1).size()); - assertEquals(1, groupTree.getDevices(3).size()); - assertEquals(2, groupTree.getDevices(4).size()); - } - -} From 301a643d62ccce053a1eaf10e5516a3d5bcc815f Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sun, 31 Mar 2024 08:13:18 -0700 Subject: [PATCH 38/42] Extract position logger --- .../java/org/traccar/ProcessingHandler.java | 8 +- .../handler/network/MainEventHandler.java | 69 +------------- .../org/traccar/helper/PositionLogger.java | 94 +++++++++++++++++++ 3 files changed, 102 insertions(+), 69 deletions(-) create mode 100644 src/main/java/org/traccar/helper/PositionLogger.java diff --git a/src/main/java/org/traccar/ProcessingHandler.java b/src/main/java/org/traccar/ProcessingHandler.java index 09ec79f9d95..688389d9810 100644 --- a/src/main/java/org/traccar/ProcessingHandler.java +++ b/src/main/java/org/traccar/ProcessingHandler.java @@ -51,6 +51,7 @@ import org.traccar.handler.events.MotionEventHandler; import org.traccar.handler.events.OverspeedEventHandler; import org.traccar.handler.network.AcknowledgementHandler; +import org.traccar.helper.PositionLogger; import org.traccar.model.Position; import java.util.List; @@ -64,13 +65,16 @@ public class ProcessingHandler extends ChannelInboundHandlerAdapter { private final NotificationManager notificationManager; + private final PositionLogger positionLogger; private final List positionHandlers; private final List eventHandlers; private final PostProcessHandler postProcessHandler; @Inject - public ProcessingHandler(Injector injector, NotificationManager notificationManager) { + public ProcessingHandler( + Injector injector, NotificationManager notificationManager, PositionLogger positionLogger) { this.notificationManager = notificationManager; + this.positionLogger = positionLogger; positionHandlers = Stream.of( TimeHandler.class, @@ -145,7 +149,7 @@ private void processEventHandlers(ChannelHandlerContext ctx, Position position) private void finishedProcessing(ChannelHandlerContext ctx, Position position) { postProcessHandler.handlePosition(position, p -> { - ctx.fireChannelRead(p); + positionLogger.log(ctx, p); ctx.writeAndFlush(new AcknowledgementHandler.EventHandled(p)); }); } diff --git a/src/main/java/org/traccar/handler/network/MainEventHandler.java b/src/main/java/org/traccar/handler/network/MainEventHandler.java index d6407f1e5cd..f600041263d 100644 --- a/src/main/java/org/traccar/handler/network/MainEventHandler.java +++ b/src/main/java/org/traccar/handler/network/MainEventHandler.java @@ -30,16 +30,11 @@ import org.traccar.BaseProtocolDecoder; import org.traccar.config.Config; import org.traccar.config.Keys; -import org.traccar.helper.DateUtil; import org.traccar.helper.NetworkUtil; -import org.traccar.model.Device; -import org.traccar.model.Position; import org.traccar.session.ConnectionManager; -import org.traccar.session.cache.CacheManager; import java.util.Arrays; import java.util.HashSet; -import java.util.LinkedHashSet; import java.util.Set; @Singleton @@ -48,76 +43,16 @@ public class MainEventHandler extends ChannelInboundHandlerAdapter { private static final Logger LOGGER = LoggerFactory.getLogger(MainEventHandler.class); - private final Set connectionlessProtocols = new HashSet<>(); - private final Set logAttributes = new LinkedHashSet<>(); - - private final CacheManager cacheManager; private final ConnectionManager connectionManager; + private final Set connectionlessProtocols = new HashSet<>(); @Inject - public MainEventHandler( - Config config, CacheManager cacheManager, ConnectionManager connectionManager) { - this.cacheManager = cacheManager; + public MainEventHandler(Config config, ConnectionManager connectionManager) { this.connectionManager = connectionManager; String connectionlessProtocolList = config.getString(Keys.STATUS_IGNORE_OFFLINE); if (connectionlessProtocolList != null) { connectionlessProtocols.addAll(Arrays.asList(connectionlessProtocolList.split("[, ]"))); } - logAttributes.addAll(Arrays.asList(config.getString(Keys.LOGGER_ATTRIBUTES).split("[, ]"))); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - if (msg instanceof Position) { - - Position position = (Position) msg; - Device device = cacheManager.getObject(Device.class, position.getDeviceId()); - - StringBuilder builder = new StringBuilder(); - builder.append("[").append(NetworkUtil.session(ctx.channel())).append("] "); - builder.append("id: ").append(device.getUniqueId()); - for (String attribute : logAttributes) { - switch (attribute) { - case "time": - builder.append(", time: ").append(DateUtil.formatDate(position.getFixTime(), false)); - break; - case "position": - builder.append(", lat: ").append(String.format("%.5f", position.getLatitude())); - builder.append(", lon: ").append(String.format("%.5f", position.getLongitude())); - break; - case "speed": - if (position.getSpeed() > 0) { - builder.append(", speed: ").append(String.format("%.1f", position.getSpeed())); - } - break; - case "course": - builder.append(", course: ").append(String.format("%.1f", position.getCourse())); - break; - case "accuracy": - if (position.getAccuracy() > 0) { - builder.append(", accuracy: ").append(String.format("%.1f", position.getAccuracy())); - } - break; - case "outdated": - if (position.getOutdated()) { - builder.append(", outdated"); - } - break; - case "invalid": - if (!position.getValid()) { - builder.append(", invalid"); - } - break; - default: - Object value = position.getAttributes().get(attribute); - if (value != null) { - builder.append(", ").append(attribute).append(": ").append(value); - } - break; - } - } - LOGGER.info(builder.toString()); - } } @Override diff --git a/src/main/java/org/traccar/helper/PositionLogger.java b/src/main/java/org/traccar/helper/PositionLogger.java new file mode 100644 index 00000000000..9f149edf4f4 --- /dev/null +++ b/src/main/java/org/traccar/helper/PositionLogger.java @@ -0,0 +1,94 @@ +/* + * Copyright 2024 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.helper; + +import io.netty.channel.ChannelHandlerContext; +import jakarta.inject.Inject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.model.Device; +import org.traccar.model.Position; +import org.traccar.session.cache.CacheManager; + +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.Set; + +public class PositionLogger { + + private static final Logger LOGGER = LoggerFactory.getLogger(PositionLogger.class); + + private final CacheManager cacheManager; + private final Set logAttributes = new LinkedHashSet<>(); + + @Inject + public PositionLogger(Config config, CacheManager cacheManager) { + this.cacheManager = cacheManager; + logAttributes.addAll(Arrays.asList(config.getString(Keys.LOGGER_ATTRIBUTES).split("[, ]"))); + } + + public void log(ChannelHandlerContext context, Position position) { + Device device = cacheManager.getObject(Device.class, position.getDeviceId()); + + StringBuilder builder = new StringBuilder(); + builder.append("[").append(NetworkUtil.session(context.channel())).append("] "); + builder.append("id: ").append(device.getUniqueId()); + for (String attribute : logAttributes) { + switch (attribute) { + case "time": + builder.append(", time: ").append(DateUtil.formatDate(position.getFixTime(), false)); + break; + case "position": + builder.append(", lat: ").append(String.format("%.5f", position.getLatitude())); + builder.append(", lon: ").append(String.format("%.5f", position.getLongitude())); + break; + case "speed": + if (position.getSpeed() > 0) { + builder.append(", speed: ").append(String.format("%.1f", position.getSpeed())); + } + break; + case "course": + builder.append(", course: ").append(String.format("%.1f", position.getCourse())); + break; + case "accuracy": + if (position.getAccuracy() > 0) { + builder.append(", accuracy: ").append(String.format("%.1f", position.getAccuracy())); + } + break; + case "outdated": + if (position.getOutdated()) { + builder.append(", outdated"); + } + break; + case "invalid": + if (!position.getValid()) { + builder.append(", invalid"); + } + break; + default: + Object value = position.getAttributes().get(attribute); + if (value != null) { + builder.append(", ").append(attribute).append(": ").append(value); + } + break; + } + } + LOGGER.info(builder.toString()); + } + +} From ae91b2f07e88d2dea97aa4bb316cb00116b4a702 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sun, 31 Mar 2024 08:16:28 -0700 Subject: [PATCH 39/42] Implement position buffering --- debug.xml | 3 + .../java/org/traccar/ProcessingHandler.java | 17 ++- src/main/java/org/traccar/config/Keys.java | 8 ++ .../traccar/database/BufferingManager.java | 103 ++++++++++++++++++ 4 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/traccar/database/BufferingManager.java diff --git a/debug.xml b/debug.xml index 028e9210b1a..f3abbe9a934 100644 --- a/debug.xml +++ b/debug.xml @@ -25,6 +25,9 @@ sa + + true 6037 diff --git a/src/main/java/org/traccar/ProcessingHandler.java b/src/main/java/org/traccar/ProcessingHandler.java index 688389d9810..cbb375a722b 100644 --- a/src/main/java/org/traccar/ProcessingHandler.java +++ b/src/main/java/org/traccar/ProcessingHandler.java @@ -21,12 +21,13 @@ import io.netty.channel.ChannelInboundHandlerAdapter; import jakarta.inject.Inject; import jakarta.inject.Singleton; +import org.traccar.config.Config; +import org.traccar.database.BufferingManager; import org.traccar.database.NotificationManager; import org.traccar.handler.BasePositionHandler; import org.traccar.handler.ComputedAttributesHandler; import org.traccar.handler.CopyAttributesHandler; import org.traccar.handler.DatabaseHandler; -import org.traccar.handler.PostProcessHandler; import org.traccar.handler.DistanceHandler; import org.traccar.handler.EngineHoursHandler; import org.traccar.handler.FilterHandler; @@ -36,6 +37,7 @@ import org.traccar.handler.HemisphereHandler; import org.traccar.handler.MotionHandler; import org.traccar.handler.PositionForwardingHandler; +import org.traccar.handler.PostProcessHandler; import org.traccar.handler.SpeedLimitHandler; import org.traccar.handler.TimeHandler; import org.traccar.handler.events.AlertEventHandler; @@ -62,19 +64,21 @@ @Singleton @ChannelHandler.Sharable -public class ProcessingHandler extends ChannelInboundHandlerAdapter { +public class ProcessingHandler extends ChannelInboundHandlerAdapter implements BufferingManager.Callback { private final NotificationManager notificationManager; private final PositionLogger positionLogger; + private final BufferingManager bufferingManager; private final List positionHandlers; private final List eventHandlers; private final PostProcessHandler postProcessHandler; @Inject public ProcessingHandler( - Injector injector, NotificationManager notificationManager, PositionLogger positionLogger) { + Injector injector, Config config, NotificationManager notificationManager, PositionLogger positionLogger) { this.notificationManager = notificationManager; this.positionLogger = positionLogger; + bufferingManager = new BufferingManager(config, this); positionHandlers = Stream.of( TimeHandler.class, @@ -117,12 +121,17 @@ public ProcessingHandler( @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof Position) { - processPositionHandlers(ctx, (Position) msg); + bufferingManager.accept(ctx, (Position) msg); } else { super.channelRead(ctx, msg); } } + @Override + public void onReleased(ChannelHandlerContext context, Position position) { + processPositionHandlers(context, position); + } + private void processPositionHandlers(ChannelHandlerContext ctx, Position position) { var iterator = positionHandlers.iterator(); iterator.next().handlePosition(position, new BasePositionHandler.Callback() { diff --git a/src/main/java/org/traccar/config/Keys.java b/src/main/java/org/traccar/config/Keys.java index 4aacb2cd860..d346084bd39 100644 --- a/src/main/java/org/traccar/config/Keys.java +++ b/src/main/java/org/traccar/config/Keys.java @@ -292,6 +292,14 @@ private Keys() { List.of(KeyType.CONFIG, KeyType.DEVICE), false); + /** + * If not zero, enable buffering of incoming data to handle ordering locations. The value is threshold for + * buffering in milliseconds. + */ + public static final ConfigKey SERVER_BUFFERING_THRESHOLD = new LongConfigKey( + "server.buffering.threshold", + List.of(KeyType.CONFIG)); + /** * Server wide connection timeout value in seconds. See protocol timeout for more information. */ diff --git a/src/main/java/org/traccar/database/BufferingManager.java b/src/main/java/org/traccar/database/BufferingManager.java new file mode 100644 index 00000000000..ab71b286009 --- /dev/null +++ b/src/main/java/org/traccar/database/BufferingManager.java @@ -0,0 +1,103 @@ +/* + * Copyright 2024 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.database; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.util.HashedWheelTimer; +import io.netty.util.Timeout; +import io.netty.util.Timer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.model.Position; + +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeSet; +import java.util.concurrent.TimeUnit; + +public class BufferingManager { + + private static final Logger LOGGER = LoggerFactory.getLogger(BufferingManager.class); + + public interface Callback { + void onReleased(ChannelHandlerContext context, Position position); + } + + private static class Holder implements Comparable { + + private static final Comparator COMPARATOR = Comparator + .comparing(Position::getFixTime) + .thenComparing(Position::getDeviceTime) + .thenComparing(Position::getServerTime); + + private final ChannelHandlerContext context; + private final Position position; + private Timeout timeout; + + private Holder(ChannelHandlerContext context, Position position) { + this.context = context; + this.position = position; + } + + @Override + public int compareTo(Holder other) { + return COMPARATOR.compare(position, other.position); + } + } + + private final Timer timer = new HashedWheelTimer(); + private final Callback callback; + private final long threshold; + + private final Map> buffer = new HashMap<>(); + + public BufferingManager(Config config, Callback callback) { + this.callback = callback; + threshold = config.getLong(Keys.SERVER_BUFFERING_THRESHOLD); + } + + private Timeout scheduleTimeout(Holder holder) { + return timer.newTimeout( + timeout -> { + LOGGER.info("released {}", holder.position.getFixTime()); + buffer.get(holder.position.getDeviceId()).remove(holder); + callback.onReleased(holder.context, holder.position); + }, + threshold, TimeUnit.MILLISECONDS); + } + + public void accept(ChannelHandlerContext context, Position position) { + if (threshold > 0) { + synchronized (buffer) { + LOGGER.info("queued {}", position.getFixTime()); + var queue = buffer.computeIfAbsent(position.getDeviceId(), k -> new TreeSet<>()); + Holder holder = new Holder(context, position); + holder.timeout = scheduleTimeout(holder); + queue.add(holder); + queue.tailSet(holder).forEach(h -> { + h.timeout.cancel(); + h.timeout = scheduleTimeout(h); + }); + } + } else { + callback.onReleased(context, position); + } + } + +} From 783a99cdc3ef70cb3f120ba8ef158dcd32086c1a Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sun, 31 Mar 2024 08:42:47 -0700 Subject: [PATCH 40/42] Fix lint issue --- src/main/java/org/traccar/database/BufferingManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/traccar/database/BufferingManager.java b/src/main/java/org/traccar/database/BufferingManager.java index ab71b286009..3e3cf587a76 100644 --- a/src/main/java/org/traccar/database/BufferingManager.java +++ b/src/main/java/org/traccar/database/BufferingManager.java @@ -39,7 +39,7 @@ public interface Callback { void onReleased(ChannelHandlerContext context, Position position); } - private static class Holder implements Comparable { + private static final class Holder implements Comparable { private static final Comparator COMPARATOR = Comparator .comparing(Position::getFixTime) From 7de30c41131c76e6491c641cd46c76a1a3513957 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sun, 31 Mar 2024 08:52:24 -0700 Subject: [PATCH 41/42] Update Sencha CMD link --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a8e4c5369c1..c025593e1f9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,7 +34,7 @@ jobs: traccar-web/package-lock.json traccar-web/modern/package-lock.json - run: | - wget -q https://trials.sencha.com/cmd/7.6.0/SenchaCmd-7.6.0.87-linux-amd64.sh.zip + wget -q https://traccar.nyc3.digitaloceanspaces.com/download/SenchaCmd-7.6.0.87-linux-amd64.sh.zip unzip SenchaCmd-*.zip ./SenchaCmd-*.sh -q echo "$HOME/bin/Sencha/Cmd/" >> $GITHUB_PATH From e4134911f17cbe6f556a8bc4d1b3f5073474a940 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sun, 31 Mar 2024 13:39:13 -0700 Subject: [PATCH 42/42] Queue positions for processing --- .../java/org/traccar/ProcessingHandler.java | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/traccar/ProcessingHandler.java b/src/main/java/org/traccar/ProcessingHandler.java index cbb375a722b..6a97b9dea72 100644 --- a/src/main/java/org/traccar/ProcessingHandler.java +++ b/src/main/java/org/traccar/ProcessingHandler.java @@ -56,9 +56,12 @@ import org.traccar.helper.PositionLogger; import org.traccar.model.Position; +import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Queue; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -73,6 +76,12 @@ public class ProcessingHandler extends ChannelInboundHandlerAdapter implements B private final List eventHandlers; private final PostProcessHandler postProcessHandler; + private final Map> queues = new HashMap<>(); + + private synchronized Queue getQueue(long deviceId) { + return queues.computeIfAbsent(deviceId, k -> new LinkedList<>()); + } + @Inject public ProcessingHandler( Injector injector, Config config, NotificationManager notificationManager, PositionLogger positionLogger) { @@ -129,7 +138,15 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception @Override public void onReleased(ChannelHandlerContext context, Position position) { - processPositionHandlers(context, position); + Queue queue = getQueue(position.getDeviceId()); + boolean queued; + synchronized (queue) { + queued = !queue.isEmpty(); + queue.offer(position); + } + if (!queued) { + processPositionHandlers(context, position); + } } private void processPositionHandlers(ChannelHandlerContext ctx, Position position) { @@ -160,6 +177,16 @@ private void finishedProcessing(ChannelHandlerContext ctx, Position position) { postProcessHandler.handlePosition(position, p -> { positionLogger.log(ctx, p); ctx.writeAndFlush(new AcknowledgementHandler.EventHandled(p)); + + Queue queue = getQueue(position.getDeviceId()); + Position nextPosition; + synchronized (queue) { + queue.poll(); // remove current position + nextPosition = queue.peek(); + } + if (nextPosition != null) { + processPositionHandlers(ctx, nextPosition); + } }); }