diff options
16 files changed, 244 insertions, 127 deletions
diff --git a/src/main/java/org/traccar/MainModule.java b/src/main/java/org/traccar/MainModule.java index 3fec4d1e6..26654947e 100644 --- a/src/main/java/org/traccar/MainModule.java +++ b/src/main/java/org/traccar/MainModule.java @@ -46,6 +46,7 @@ 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; @@ -69,7 +70,6 @@ import org.traccar.geocoder.TestGeocoder; 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 +276,16 @@ public class MainModule extends AbstractModule { @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; @@ -386,6 +384,8 @@ public class MainModule extends AbstractModule { 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/config/Keys.java b/src/main/java/org/traccar/config/Keys.java index 02e684875..4aacb2cd8 100644 --- a/src/main/java/org/traccar/config/Keys.java +++ b/src/main/java/org/traccar/config/Keys.java @@ -1656,7 +1656,7 @@ public final class 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/forward/EventForwarderMqtt.java b/src/main/java/org/traccar/forward/EventForwarderMqtt.java index 7f4e29384..7fee495ac 100644 --- a/src/main/java/org/traccar/forward/EventForwarderMqtt.java +++ b/src/main/java/org/traccar/forward/EventForwarderMqtt.java @@ -15,86 +15,33 @@ */ 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 java.net.URI; -import java.net.URISyntaxException; -import java.util.UUID; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; 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) { - 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); + mqttClient = new MqttClient(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; try { - payload = objectMapper.writeValueAsString(eventData).getBytes(); + String payload = objectMapper.writeValueAsString(eventData); + mqttClient.publish(topic, payload, (message, e) -> resultHandler.onResult(e == null, e)); } 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)); } } diff --git a/src/main/java/org/traccar/forward/MqttClient.java b/src/main/java/org/traccar/forward/MqttClient.java new file mode 100644 index 000000000..416a167ec --- /dev/null +++ b/src/main/java/org/traccar/forward/MqttClient.java @@ -0,0 +1,76 @@ +/* + * 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.forward; + +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.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 class MqttClient { + + private final Mqtt5AsyncClient client; + + MqttClient(String url) { + URI uri; + try { + uri = new URI(url); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + + 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(); + + client = builder.buildAsync(); + client.connectWith().send().whenComplete((message, e) -> { + throw new RuntimeException(e); + }); + } + + private Mqtt5SimpleAuth getSimpleAuth(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 = Mqtt5SimpleAuth.builder().username(userInfo.substring(0, delimiter++)) + .password(userInfo.substring(delimiter).getBytes()).build(); + } + } + return simpleAuth; + } + + public void publish( + String pubTopic, String payload, BiConsumer<? super Mqtt5PublishResult, ? super Throwable> 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 new file mode 100644 index 000000000..53f0ced19 --- /dev/null +++ b/src/main/java/org/traccar/forward/PositionForwarderMqtt.java @@ -0,0 +1,47 @@ +/* + * 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.forward; + +import org.traccar.config.Config; +import org.traccar.config.Keys; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class PositionForwarderMqtt implements PositionForwarder { + + private final MqttClient mqttClient; + private final ObjectMapper objectMapper; + + private final String topic; + + public PositionForwarderMqtt(final Config config, final ObjectMapper objectMapper) { + this.topic = config.getString(Keys.FORWARD_TOPIC); + mqttClient = new MqttClient(config.getString(Keys.FORWARD_URL)); + this.objectMapper = objectMapper; + } + + @Override + public void forward(PositionData positionData, ResultHandler resultHandler) { + try { + String payload = objectMapper.writeValueAsString(topic); + 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/geolocation/MozillaGeolocationProvider.java b/src/main/java/org/traccar/geolocation/MozillaGeolocationProvider.java deleted file mode 100644 index 7eb22dcca..000000000 --- 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/main/java/org/traccar/handler/StandardLoggingHandler.java b/src/main/java/org/traccar/handler/StandardLoggingHandler.java index 5978d632e..513602dd8 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.helper.NetworkUtil; 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 class StandardLoggingHandler extends ChannelDuplexHandler { @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 class StandardLoggingHandler extends ChannelDuplexHandler { @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 c19163af3..b04f51b32 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/protocol/AutoTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/AutoTrackProtocolDecoder.java index c072e55d0..938d170e6 100644 --- a/src/main/java/org/traccar/protocol/AutoTrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/AutoTrackProtocolDecoder.java @@ -101,10 +101,11 @@ public class AutoTrackProtocolDecoder extends BaseProtocolDecoder { 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; } diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java index 0a8540543..2186fb91f 100644 --- a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java @@ -678,7 +678,8 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { 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/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java index 64373e344..2d6bde89f 100644 --- a/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java @@ -295,6 +295,13 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_HEART_RATE, heartRate); } break; + case 0x41: + buf.readUnsignedIntLE(); // timestamp + int spO2 = buf.readUnsignedByte(); + if (spO2 > 1) { + position.set("spO2", spO2); + } + break; default: break; } 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 000000000..3b7e2ebf8 --- /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 42dcf5ce9..8431a0327 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<Long, DeviceSession> sessionsByDeviceId = new ConcurrentHashMap<>(); - private final Map<SocketAddress, Map<String, DeviceSession>> sessionsByEndpoint = new ConcurrentHashMap<>(); - private final Map<SocketAddress, String> unknownByEndpoint = new ConcurrentHashMap<>(); + private final Map<ConnectionKey, Map<String, DeviceSession>> sessionsByEndpoint = new ConcurrentHashMap<>(); + private final Map<ConnectionKey, String> unknownByEndpoint = new ConcurrentHashMap<>(); private final Config config; private final CacheManager cacheManager; @@ -108,8 +108,9 @@ public class ConnectionManager implements BroadcastInterface { Protocol protocol, Channel channel, SocketAddress remoteAddress, String... uniqueIds) throws Exception { + ConnectionKey connectionKey = new ConnectionKey(channel, remoteAddress); Map<String, DeviceSession> 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 class ConnectionManager implements BroadcastInterface { } if (device != null) { - unknownByEndpoint.remove(remoteAddress); + unknownByEndpoint.remove(connectionKey); device.checkDisabled(); DeviceSession oldSession = sessionsByDeviceId.remove(device.getId()); if (oldSession != null) { - Map<String, DeviceSession> oldEndpointSessions = sessionsByEndpoint.get(oldSession.getRemoteAddress()); + Map<String, DeviceSession> 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 class ConnectionManager implements BroadcastInterface { 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 @@ public class ConnectionManager implements BroadcastInterface { public void deviceDisconnected(Channel channel, boolean supportsOffline) { SocketAddress remoteAddress = channel.remoteAddress(); if (remoteAddress != null) { - Map<String, DeviceSession> endpointSessions = sessionsByEndpoint.remove(remoteAddress); + ConnectionKey connectionKey = new ConnectionKey(channel, remoteAddress); + Map<String, DeviceSession> endpointSessions = sessionsByEndpoint.remove(connectionKey); if (endpointSessions != null) { for (DeviceSession deviceSession : endpointSessions.values()) { if (supportsOffline) { @@ -199,7 +201,7 @@ public class ConnectionManager implements BroadcastInterface { cacheManager.removeDevice(deviceSession.getDeviceId()); } } - unknownByEndpoint.remove(remoteAddress); + unknownByEndpoint.remove(connectionKey); } } @@ -212,7 +214,7 @@ public class ConnectionManager implements BroadcastInterface { 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 class ConnectionManager implements BroadcastInterface { } 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 f124ca7f9..31d5e6a13 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 class DeviceSession { return channel; } - public SocketAddress getRemoteAddress() { - return remoteAddress; + public ConnectionKey getConnectionKey() { + return new ConnectionKey(channel, remoteAddress); } public boolean supportsLiveCommands() { diff --git a/src/test/java/org/traccar/geolocation/GeolocationProviderTest.java b/src/test/java/org/traccar/geolocation/GeolocationProviderTest.java index da5ae3340..c1fa6dbbe 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)); diff --git a/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java index 164635109..7fd7cc599 100644 --- a/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java @@ -12,6 +12,10 @@ public class HuabaoProtocolDecoderTest extends ProtocolTest { var decoder = inject(new HuabaoProtocolDecoder(null)); verifyAttribute(decoder, binary( + "7E0200005300959000194400080000000000000003015DA64806CCB4A8001100000000230816014137010400005B3F30011F310112EB29000C00B28986049910219020787400060089FFFFFFFF000600C5FFFFFFEF0004002D0F4E000300A84CE07E"), + Position.KEY_BATTERY_LEVEL, 76); + + verifyAttribute(decoder, binary( "7e55018c418560090010174701022106242122348476113550490700001c06000000074e0000000000000308100000100102020200000a030000000b0803363839363037650c0600000001000afa7e"), "unlockResult", 0x65); |