diff options
author | Yuriy Piskarev <yuriy.piskarev@gmail.com> | 2023-08-24 14:16:17 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-08-24 14:16:17 +0300 |
commit | ae406c7b49a72de24d81fd74386d9638342c90ee (patch) | |
tree | 6fbcf557375b98e926c78af9c757e62c79d72a1b /src | |
parent | 56ff656c908b19feb2fa3dcffa48cc3bcdfe9b3b (diff) | |
parent | 9aeedc90da24848ff97227d6f281eb4d1e1506ef (diff) | |
download | trackermap-server-ae406c7b49a72de24d81fd74386d9638342c90ee.tar.gz trackermap-server-ae406c7b49a72de24d81fd74386d9638342c90ee.tar.bz2 trackermap-server-ae406c7b49a72de24d81fd74386d9638342c90ee.zip |
Merge branch 'traccar:master' into master
Diffstat (limited to 'src')
1210 files changed, 24516 insertions, 12530 deletions
diff --git a/src/main/java/org/traccar/BasePipelineFactory.java b/src/main/java/org/traccar/BasePipelineFactory.java index 89ef76a80..5b48f3d15 100644 --- a/src/main/java/org/traccar/BasePipelineFactory.java +++ b/src/main/java/org/traccar/BasePipelineFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 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. @@ -15,6 +15,7 @@ */ package org.traccar; +import com.google.inject.Injector; import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelInboundHandler; @@ -22,7 +23,9 @@ import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOutboundHandler; import io.netty.channel.ChannelPipeline; 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; @@ -30,9 +33,11 @@ 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; @@ -43,10 +48,11 @@ 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.FuelDropEventHandler; +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; @@ -54,16 +60,22 @@ import java.util.Map; public abstract class BasePipelineFactory extends ChannelInitializer<Channel> { + private final Injector injector; private final TrackerConnector connector; + private final Config config; private final String protocol; - private int timeout; + private final int timeout; - public BasePipelineFactory(TrackerConnector connector, String protocol) { + public BasePipelineFactory(TrackerConnector connector, Config config, String protocol) { + this.injector = Main.getInjector(); this.connector = connector; + this.config = config; this.protocol = protocol; - timeout = Context.getConfig().getInteger(Keys.PROTOCOL_TIMEOUT.withPrefix(protocol)); + int timeout = config.getInteger(Keys.PROTOCOL_TIMEOUT.withPrefix(protocol)); if (timeout == 0) { - timeout = Context.getConfig().getInteger(Keys.SERVER_TIMEOUT); + this.timeout = config.getInteger(Keys.SERVER_TIMEOUT); + } else { + this.timeout = timeout; } } @@ -75,7 +87,7 @@ public abstract class BasePipelineFactory extends ChannelInitializer<Channel> { private void addHandlers(ChannelPipeline pipeline, Class<? extends ChannelHandler>... handlerClasses) { for (Class<? extends ChannelHandler> handlerClass : handlerClasses) { if (handlerClass != null) { - pipeline.addLast(Main.getInjector().getInstance(handlerClass)); + pipeline.addLast(injector.getInstance(handlerClass)); } } } @@ -105,11 +117,22 @@ public abstract class BasePipelineFactory extends ChannelInitializer<Channel> { pipeline.addLast(new IdleStateHandler(timeout, 0, 0)); } 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(new NetworkMessageHandler()); pipeline.addLast(new StandardLoggingHandler(protocol)); + if (!connector.isDatagram() && !config.getBoolean(Keys.SERVER_INSTANT_ACKNOWLEDGEMENT)) { + pipeline.addLast(new AcknowledgementHandler()); + } addProtocolHandlers(handler -> { - if (!(handler instanceof BaseProtocolDecoder || handler instanceof BaseProtocolEncoder)) { + if (handler instanceof BaseProtocolDecoder || handler instanceof BaseProtocolEncoder) { + injector.injectMembers(handler); + } else { if (handler instanceof ChannelInboundHandler) { handler = new WrapperInboundHandler((ChannelInboundHandler) handler); } else { @@ -127,26 +150,27 @@ public abstract class BasePipelineFactory extends ChannelInitializer<Channel> { DistanceHandler.class, RemoteAddressHandler.class, FilterHandler.class, + GeofenceHandler.class, GeocoderHandler.class, SpeedLimitHandler.class, MotionHandler.class, CopyAttributesHandler.class, EngineHoursHandler.class, ComputedAttributesHandler.class, - WebDataHandler.class, + PositionForwardingHandler.class, DefaultDataHandler.class, + MediaEventHandler.class, CommandResultEventHandler.class, OverspeedEventHandler.class, BehaviorEventHandler.class, - FuelDropEventHandler.class, + FuelEventHandler.class, MotionEventHandler.class, GeofenceEventHandler.class, AlertEventHandler.class, IgnitionEventHandler.class, MaintenanceEventHandler.class, - DriverEventHandler.class); - - pipeline.addLast(new MainEventHandler()); + DriverEventHandler.class, + MainEventHandler.class); } } diff --git a/src/main/java/org/traccar/BaseProtocol.java b/src/main/java/org/traccar/BaseProtocol.java index 52d34dc44..ea302997c 100644 --- a/src/main/java/org/traccar/BaseProtocol.java +++ b/src/main/java/org/traccar/BaseProtocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org) + * 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. @@ -21,7 +21,10 @@ import io.netty.channel.Channel; import io.netty.handler.codec.string.StringEncoder; import org.traccar.helper.DataConverter; import org.traccar.model.Command; +import org.traccar.sms.SmsManager; +import jakarta.annotation.Nullable; +import jakarta.inject.Inject; import java.net.SocketAddress; import java.util.Arrays; import java.util.Collection; @@ -37,6 +40,8 @@ public abstract class BaseProtocol implements Protocol { private final Set<String> supportedTextCommands = new HashSet<>(); private final List<TrackerConnector> connectorList = new LinkedList<>(); + private SmsManager smsManager; + private StringProtocolEncoder textCommandEncoder = null; public static String nameFromClass(Class<?> clazz) { @@ -48,6 +53,11 @@ public abstract class BaseProtocol implements Protocol { name = nameFromClass(getClass()); } + @Inject + public void setSmsManager(@Nullable SmsManager smsManager) { + this.smsManager = smsManager; + } + @Override public String getName() { return name; @@ -95,7 +105,8 @@ public abstract class BaseProtocol implements Protocol { } else if (command.getType().equals(Command.TYPE_CUSTOM)) { String data = command.getString(Command.KEY_DATA); if (BasePipelineFactory.getHandler(channel.pipeline(), StringEncoder.class) != null) { - channel.writeAndFlush(new NetworkMessage(data, remoteAddress)); + channel.writeAndFlush(new NetworkMessage( + data.replace("\\r", "\r").replace("\\n", "\n"), remoteAddress)); } else { ByteBuf buf = Unpooled.wrappedBuffer(DataConverter.parseHex(data)); channel.writeAndFlush(new NetworkMessage(buf, remoteAddress)); @@ -111,13 +122,13 @@ public abstract class BaseProtocol implements Protocol { @Override public void sendTextCommand(String destAddress, Command command) throws Exception { - if (Context.getSmsManager() != null) { + if (smsManager != null) { if (command.getType().equals(Command.TYPE_CUSTOM)) { - Context.getSmsManager().sendMessageSync(destAddress, command.getString(Command.KEY_DATA), true); + smsManager.sendMessage(destAddress, command.getString(Command.KEY_DATA), true); } else if (supportedTextCommands.contains(command.getType()) && textCommandEncoder != null) { String encodedCommand = (String) textCommandEncoder.encodeCommand(command); if (encodedCommand != null) { - Context.getSmsManager().sendMessageSync(destAddress, encodedCommand, true); + smsManager.sendMessage(destAddress, encodedCommand, true); } else { throw new RuntimeException("Failed to encode command"); } diff --git a/src/main/java/org/traccar/BaseProtocolDecoder.java b/src/main/java/org/traccar/BaseProtocolDecoder.java index a40756796..69ca0ccc6 100644 --- a/src/main/java/org/traccar/BaseProtocolDecoder.java +++ b/src/main/java/org/traccar/BaseProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,47 +15,82 @@ */ package org.traccar; +import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; -import io.netty.channel.socket.DatagramChannel; -import io.netty.handler.codec.http.HttpRequestDecoder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.database.CommandsManager; -import org.traccar.database.ConnectionManager; -import org.traccar.database.IdentityManager; +import org.traccar.database.MediaManager; import org.traccar.database.StatisticsManager; import org.traccar.helper.UnitsConverter; +import org.traccar.helper.model.AttributeUtil; import org.traccar.model.Command; import org.traccar.model.Device; import org.traccar.model.Position; +import org.traccar.session.ConnectionManager; +import org.traccar.session.DeviceSession; +import org.traccar.session.cache.CacheManager; +import org.traccar.storage.StorageException; +import jakarta.inject.Inject; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.Collection; import java.util.Date; -import java.util.HashMap; import java.util.HashSet; -import java.util.Map; import java.util.Set; import java.util.TimeZone; public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder { - private static final Logger LOGGER = LoggerFactory.getLogger(BaseProtocolDecoder.class); - private static final String PROTOCOL_UNKNOWN = "unknown"; - private final Config config = Context.getConfig(); - private final IdentityManager identityManager = Context.getIdentityManager(); - private final ConnectionManager connectionManager = Context.getConnectionManager(); - private final StatisticsManager statisticsManager; private final Protocol protocol; + private CacheManager cacheManager; + private ConnectionManager connectionManager; + private StatisticsManager statisticsManager; + private MediaManager mediaManager; + private CommandsManager commandsManager; + public BaseProtocolDecoder(Protocol protocol) { this.protocol = protocol; - statisticsManager = Main.getInjector() != null ? Main.getInjector().getInstance(StatisticsManager.class) : null; + } + + public CacheManager getCacheManager() { + return cacheManager; + } + + @Inject + public void setCacheManager(CacheManager cacheManager) { + this.cacheManager = cacheManager; + } + + @Inject + public void setConnectionManager(ConnectionManager connectionManager) { + this.connectionManager = connectionManager; + } + + @Inject + public void setStatisticsManager(StatisticsManager statisticsManager) { + this.statisticsManager = statisticsManager; + } + + @Inject + public void setMediaManager(MediaManager mediaManager) { + this.mediaManager = mediaManager; + } + + @Inject + public void setCommandsManager(CommandsManager commandsManager) { + this.commandsManager = commandsManager; + } + + public CommandsManager getCommandsManager() { + return commandsManager; + } + + public String writeMediaFile(String uniqueId, ByteBuf buf, String extension) { + return mediaManager.writeFile(uniqueId, buf, extension); } public String getProtocolName() { @@ -63,7 +98,7 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder { } public String getServer(Channel channel, char delimiter) { - String server = config.getString(Keys.PROTOCOL_SERVER.withPrefix(getProtocolName())); + String server = getConfig().getString(Keys.PROTOCOL_SERVER.withPrefix(getProtocolName())); if (server == null && channel != null) { InetSocketAddress address = (InetSocketAddress) channel.localAddress(); server = address.getAddress().getHostAddress() + ":" + address.getPort(); @@ -72,7 +107,7 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder { } protected double convertSpeed(double value, String defaultUnits) { - switch (config.getString(getProtocolName() + ".speed", defaultUnits)) { + switch (getConfig().getString(getProtocolName() + ".speed", defaultUnits)) { case "kmh": return UnitsConverter.knotsFromKph(value); case "mps": @@ -91,101 +126,18 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder { protected TimeZone getTimeZone(long deviceId, String defaultTimeZone) { TimeZone result = TimeZone.getTimeZone(defaultTimeZone); - String timeZoneName = identityManager.lookupAttributeString(deviceId, "decoder.timezone", null, false, true); + String timeZoneName = AttributeUtil.lookup(cacheManager, Keys.DECODER_TIMEZONE, deviceId); if (timeZoneName != null) { result = TimeZone.getTimeZone(timeZoneName); } return result; } - private DeviceSession channelDeviceSession; // connection-based protocols - private final Map<SocketAddress, DeviceSession> addressDeviceSessions = new HashMap<>(); // connectionless protocols - - private long findDeviceId(SocketAddress remoteAddress, String... uniqueIds) { - if (uniqueIds.length > 0) { - long deviceId = 0; - Device device = null; - try { - for (String uniqueId : uniqueIds) { - if (uniqueId != null) { - device = identityManager.getByUniqueId(uniqueId); - if (device != null) { - deviceId = device.getId(); - break; - } - } - } - } catch (Exception e) { - LOGGER.warn("Find device error", e); - } - if (deviceId == 0 && config.getBoolean(Keys.DATABASE_REGISTER_UNKNOWN)) { - return identityManager.addUnknownDevice(uniqueIds[0]); - } - if (device != null && !device.getDisabled()) { - return deviceId; - } - StringBuilder message = new StringBuilder(); - if (deviceId == 0) { - message.append("Unknown device -"); - } else { - message.append("Disabled device -"); - } - for (String uniqueId : uniqueIds) { - message.append(" ").append(uniqueId); - } - if (remoteAddress != null) { - message.append(" (").append(((InetSocketAddress) remoteAddress).getHostString()).append(")"); - } - LOGGER.warn(message.toString()); - } - return 0; - } - public DeviceSession getDeviceSession(Channel channel, SocketAddress remoteAddress, String... uniqueIds) { - return getDeviceSession(channel, remoteAddress, false, uniqueIds); - } - - public DeviceSession getDeviceSession( - Channel channel, SocketAddress remoteAddress, boolean ignoreCache, String... uniqueIds) { - if (channel != null && BasePipelineFactory.getHandler(channel.pipeline(), HttpRequestDecoder.class) != null - || ignoreCache || config.getBoolean(Keys.PROTOCOL_IGNORE_SESSIONS_CACHE.withPrefix(getProtocolName())) - || config.getBoolean(Keys.DECODER_IGNORE_SESSIONS_CACHE)) { - long deviceId = findDeviceId(remoteAddress, uniqueIds); - if (deviceId != 0) { - if (connectionManager != null) { - connectionManager.addActiveDevice(deviceId, protocol, channel, remoteAddress); - } - return new DeviceSession(deviceId); - } else { - return null; - } - } - if (channel instanceof DatagramChannel) { - long deviceId = findDeviceId(remoteAddress, uniqueIds); - DeviceSession deviceSession = addressDeviceSessions.get(remoteAddress); - if (deviceSession != null && (deviceSession.getDeviceId() == deviceId || uniqueIds.length == 0)) { - return deviceSession; - } else if (deviceId != 0) { - deviceSession = new DeviceSession(deviceId); - addressDeviceSessions.put(remoteAddress, deviceSession); - if (connectionManager != null) { - connectionManager.addActiveDevice(deviceId, protocol, channel, remoteAddress); - } - return deviceSession; - } else { - return null; - } - } else { - if (channelDeviceSession == null) { - long deviceId = findDeviceId(remoteAddress, uniqueIds); - if (deviceId != 0) { - channelDeviceSession = new DeviceSession(deviceId); - if (connectionManager != null) { - connectionManager.addActiveDevice(deviceId, protocol, channel, remoteAddress); - } - } - } - return channelDeviceSession; + try { + return connectionManager.getDeviceSession(protocol, channel, remoteAddress, uniqueIds); + } catch (StorageException e) { + throw new RuntimeException(e); } } @@ -193,7 +145,7 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder { if (position.getDeviceId() != 0) { position.setOutdated(true); - Position last = identityManager.getLastPosition(position.getDeviceId()); + Position last = cacheManager.getPosition(position.getDeviceId()); if (last != null) { position.setFixTime(last.getFixTime()); position.setValid(last.getValid()); @@ -245,18 +197,15 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder { } protected void sendQueuedCommands(Channel channel, SocketAddress remoteAddress, long deviceId) { - CommandsManager commandsManager = Context.getCommandsManager(); - if (commandsManager != null) { - for (Command command : commandsManager.readQueuedCommands(deviceId)) { - protocol.sendDataCommand(channel, remoteAddress, command); - } + for (Command command : commandsManager.readQueuedCommands(deviceId)) { + protocol.sendDataCommand(channel, remoteAddress, command); } } @Override protected Object handleEmptyMessage(Channel channel, SocketAddress remoteAddress, Object msg) { DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); - if (config.getBoolean(Keys.DATABASE_SAVE_EMPTY) && deviceSession != null) { + if (getConfig().getBoolean(Keys.DATABASE_SAVE_EMPTY) && deviceSession != null) { Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); getLastLocation(position, null); diff --git a/src/main/java/org/traccar/BaseProtocolEncoder.java b/src/main/java/org/traccar/BaseProtocolEncoder.java index b6df07b98..b9ca16838 100644 --- a/src/main/java/org/traccar/BaseProtocolEncoder.java +++ b/src/main/java/org/traccar/BaseProtocolEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org) + * 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. @@ -21,7 +21,13 @@ import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.ChannelPromise; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.traccar.helper.NetworkUtil; +import org.traccar.helper.model.AttributeUtil; import org.traccar.model.Command; +import org.traccar.model.Device; +import org.traccar.session.cache.CacheManager; + +import jakarta.inject.Inject; public abstract class BaseProtocolEncoder extends ChannelOutboundHandlerAdapter { @@ -31,22 +37,33 @@ public abstract class BaseProtocolEncoder extends ChannelOutboundHandlerAdapter private final Protocol protocol; + private CacheManager cacheManager; + public BaseProtocolEncoder(Protocol protocol) { this.protocol = protocol; } + public CacheManager getCacheManager() { + return cacheManager; + } + + @Inject + public void setCacheManager(CacheManager cacheManager) { + this.cacheManager = cacheManager; + } + public String getProtocolName() { return protocol != null ? protocol.getName() : PROTOCOL_UNKNOWN; } protected String getUniqueId(long deviceId) { - return Context.getIdentityManager().getById(deviceId).getUniqueId(); + return cacheManager.getObject(Device.class, deviceId).getUniqueId(); } protected void initDevicePassword(Command command, String defaultPassword) { - if (!command.getAttributes().containsKey(Command.KEY_DEVICE_PASSWORD)) { - String password = Context.getIdentityManager() - .getDevicePassword(command.getDeviceId(), getProtocolName(), defaultPassword); + if (!command.hasAttribute(Command.KEY_DEVICE_PASSWORD)) { + String password = AttributeUtil.getDevicePassword( + cacheManager, command.getDeviceId(), getProtocolName(), defaultPassword); command.set(Command.KEY_DEVICE_PASSWORD, password); } } @@ -54,31 +71,30 @@ public abstract class BaseProtocolEncoder extends ChannelOutboundHandlerAdapter @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { - NetworkMessage networkMessage = (NetworkMessage) msg; + if (msg instanceof NetworkMessage) { + NetworkMessage networkMessage = (NetworkMessage) msg; + if (networkMessage.getMessage() instanceof Command) { - if (networkMessage.getMessage() instanceof Command) { + Command command = (Command) networkMessage.getMessage(); + Object encodedCommand = encodeCommand(ctx.channel(), command); - Command command = (Command) networkMessage.getMessage(); - Object encodedCommand = encodeCommand(ctx.channel(), command); + StringBuilder s = new StringBuilder(); + s.append("[").append(NetworkUtil.session(ctx.channel())).append("] "); + s.append("id: ").append(getUniqueId(command.getDeviceId())).append(", "); + s.append("command type: ").append(command.getType()).append(" "); + if (encodedCommand != null) { + s.append("sent"); + } else { + s.append("not sent"); + } + LOGGER.info(s.toString()); - StringBuilder s = new StringBuilder(); - s.append("[").append(ctx.channel().id().asShortText()).append("] "); - s.append("id: ").append(getUniqueId(command.getDeviceId())).append(", "); - s.append("command type: ").append(command.getType()).append(" "); - if (encodedCommand != null) { - s.append("sent"); - } else { - s.append("not sent"); - } - LOGGER.info(s.toString()); - - ctx.write(new NetworkMessage(encodedCommand, networkMessage.getRemoteAddress()), promise); - - } else { - - super.write(ctx, msg, promise); + ctx.write(new NetworkMessage(encodedCommand, networkMessage.getRemoteAddress()), promise); + return; + } } + super.write(ctx, msg, promise); } protected Object encodeCommand(Channel channel, Command command) { diff --git a/src/main/java/org/traccar/Context.java b/src/main/java/org/traccar/Context.java deleted file mode 100644 index aeba9c4c9..000000000 --- a/src/main/java/org/traccar/Context.java +++ /dev/null @@ -1,416 +0,0 @@ -/* - * Copyright 2015 - 2021 Anton Tananaev (anton@traccar.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.traccar; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.datatype.jsr353.JSR353Module; -import org.apache.velocity.app.VelocityEngine; -import org.eclipse.jetty.util.URIUtil; -import org.traccar.config.Config; -import org.traccar.config.Keys; -import org.traccar.database.AttributesManager; -import org.traccar.database.BaseObjectManager; -import org.traccar.database.CalendarManager; -import org.traccar.database.CommandsManager; -import org.traccar.database.ConnectionManager; -import org.traccar.database.DataManager; -import org.traccar.database.DeviceManager; -import org.traccar.database.DriversManager; -import org.traccar.database.GeofenceManager; -import org.traccar.database.GroupsManager; -import org.traccar.database.IdentityManager; -import org.traccar.database.LdapProvider; -import org.traccar.database.MailManager; -import org.traccar.database.MaintenancesManager; -import org.traccar.database.MediaManager; -import org.traccar.database.NotificationManager; -import org.traccar.database.OrderManager; -import org.traccar.database.PermissionsManager; -import org.traccar.database.UsersManager; -import org.traccar.geocoder.Geocoder; -import org.traccar.helper.Log; -import org.traccar.helper.SanitizerModule; -import org.traccar.model.Attribute; -import org.traccar.model.BaseModel; -import org.traccar.model.Calendar; -import org.traccar.model.Command; -import org.traccar.model.Device; -import org.traccar.model.Driver; -import org.traccar.model.Geofence; -import org.traccar.model.Group; -import org.traccar.model.Maintenance; -import org.traccar.model.Notification; -import org.traccar.model.Order; -import org.traccar.model.User; -import org.traccar.notification.EventForwarder; -import org.traccar.notification.NotificatorManager; -import org.traccar.reports.model.TripsConfig; -import org.traccar.schedule.ScheduleManager; -import org.traccar.sms.HttpSmsClient; -import org.traccar.sms.SmsManager; -import org.traccar.sms.SnsSmsClient; -import org.traccar.web.WebServer; - -import javax.ws.rs.client.Client; -import javax.ws.rs.client.ClientBuilder; -import javax.ws.rs.ext.ContextResolver; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.Properties; - -public final class Context { - - private Context() { - } - - private static Config config; - - public static Config getConfig() { - return config; - } - - private static ObjectMapper objectMapper; - - public static ObjectMapper getObjectMapper() { - return objectMapper; - } - - private static IdentityManager identityManager; - - public static IdentityManager getIdentityManager() { - return identityManager; - } - - private static DataManager dataManager; - - public static DataManager getDataManager() { - return dataManager; - } - - private static LdapProvider ldapProvider; - - public static LdapProvider getLdapProvider() { - return ldapProvider; - } - - private static MailManager mailManager; - - public static MailManager getMailManager() { - return mailManager; - } - - private static MediaManager mediaManager; - - public static MediaManager getMediaManager() { - return mediaManager; - } - - private static UsersManager usersManager; - - public static UsersManager getUsersManager() { - return usersManager; - } - - private static GroupsManager groupsManager; - - public static GroupsManager getGroupsManager() { - return groupsManager; - } - - private static DeviceManager deviceManager; - - public static DeviceManager getDeviceManager() { - return deviceManager; - } - - private static ConnectionManager connectionManager; - - public static ConnectionManager getConnectionManager() { - return connectionManager; - } - - private static PermissionsManager permissionsManager; - - public static PermissionsManager getPermissionsManager() { - return permissionsManager; - } - - public static Geocoder getGeocoder() { - return Main.getInjector() != null ? Main.getInjector().getInstance(Geocoder.class) : null; - } - - private static WebServer webServer; - - public static WebServer getWebServer() { - return webServer; - } - - private static ServerManager serverManager; - - public static ServerManager getServerManager() { - return serverManager; - } - - private static ScheduleManager scheduleManager; - - public static ScheduleManager getScheduleManager() { - return scheduleManager; - } - - private static GeofenceManager geofenceManager; - - public static GeofenceManager getGeofenceManager() { - return geofenceManager; - } - - private static CalendarManager calendarManager; - - public static CalendarManager getCalendarManager() { - return calendarManager; - } - - private static NotificationManager notificationManager; - - public static NotificationManager getNotificationManager() { - return notificationManager; - } - - private static NotificatorManager notificatorManager; - - public static NotificatorManager getNotificatorManager() { - return notificatorManager; - } - - private static VelocityEngine velocityEngine; - - public static VelocityEngine getVelocityEngine() { - return velocityEngine; - } - - private static Client client = ClientBuilder.newClient(); - - public static Client getClient() { - return client; - } - - private static EventForwarder eventForwarder; - - public static EventForwarder getEventForwarder() { - return eventForwarder; - } - - private static AttributesManager attributesManager; - - public static AttributesManager getAttributesManager() { - return attributesManager; - } - - private static DriversManager driversManager; - - public static DriversManager getDriversManager() { - return driversManager; - } - - private static CommandsManager commandsManager; - - public static CommandsManager getCommandsManager() { - return commandsManager; - } - - private static MaintenancesManager maintenancesManager; - - public static MaintenancesManager getMaintenancesManager() { - return maintenancesManager; - } - - private static OrderManager orderManager; - - public static OrderManager getOrderManager() { - return orderManager; - } - - private static SmsManager smsManager; - - public static SmsManager getSmsManager() { - return smsManager; - } - - private static TripsConfig tripsConfig; - - public static TripsConfig getTripsConfig() { - return tripsConfig; - } - - public static TripsConfig initTripsConfig() { - return new TripsConfig( - config.getLong(Keys.REPORT_TRIP_MINIMAL_TRIP_DISTANCE), - config.getLong(Keys.REPORT_TRIP_MINIMAL_TRIP_DURATION) * 1000, - config.getLong(Keys.REPORT_TRIP_MINIMAL_PARKING_DURATION) * 1000, - config.getLong(Keys.REPORT_TRIP_MINIMAL_NO_DATA_DURATION) * 1000, - config.getBoolean(Keys.REPORT_TRIP_USE_IGNITION), - config.getBoolean(Keys.EVENT_MOTION_PROCESS_INVALID_POSITIONS), - config.getDouble(Keys.EVENT_MOTION_SPEED_THRESHOLD)); - } - - private static class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> { - - @Override - public ObjectMapper getContext(Class<?> clazz) { - return objectMapper; - } - - } - - public static void init(String configFile) throws Exception { - - try { - config = new Config(configFile); - Log.setupLogger(config); - } catch (Exception e) { - config = new Config(); - Log.setupDefaultLogger(); - throw e; - } - - objectMapper = new ObjectMapper(); - objectMapper.registerModule(new SanitizerModule()); - objectMapper.registerModule(new JSR353Module()); - objectMapper.setConfig( - objectMapper.getSerializationConfig().without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)); - - client = ClientBuilder.newClient().register(new ObjectMapperContextResolver()); - - if (config.hasKey(Keys.DATABASE_URL)) { - dataManager = new DataManager(config); - } - - if (config.hasKey(Keys.LDAP_URL)) { - ldapProvider = new LdapProvider(config); - } - - mailManager = new MailManager(); - - mediaManager = new MediaManager(config.getString(Keys.MEDIA_PATH)); - - if (dataManager != null) { - usersManager = new UsersManager(dataManager); - groupsManager = new GroupsManager(dataManager); - deviceManager = new DeviceManager(dataManager); - } - - identityManager = deviceManager; - - if (config.hasKey(Keys.WEB_PORT)) { - webServer = new WebServer(config); - } - - permissionsManager = new PermissionsManager(dataManager, usersManager); - - connectionManager = new ConnectionManager(); - - tripsConfig = initTripsConfig(); - - if (config.hasKey(Keys.SMS_HTTP_URL)) { - smsManager = new HttpSmsClient(); - } else if (config.hasKey(Keys.SMS_AWS_REGION)) { - smsManager = new SnsSmsClient(); - } - - initEventsModule(); - - serverManager = new ServerManager(); - scheduleManager = new ScheduleManager(); - - if (config.hasKey(Keys.EVENT_FORWARD_URL)) { - eventForwarder = new EventForwarder(); - } - - attributesManager = new AttributesManager(dataManager); - - driversManager = new DriversManager(dataManager); - - commandsManager = new CommandsManager(dataManager, config.getBoolean(Keys.COMMANDS_QUEUEING)); - - orderManager = new OrderManager(dataManager); - - } - - private static void initEventsModule() { - - geofenceManager = new GeofenceManager(dataManager); - calendarManager = new CalendarManager(dataManager); - maintenancesManager = new MaintenancesManager(dataManager); - notificationManager = new NotificationManager(dataManager); - notificatorManager = new NotificatorManager(); - Properties velocityProperties = new Properties(); - velocityProperties.setProperty("file.resource.loader.path", - Context.getConfig().getString("templates.rootPath", "templates") + "/"); - velocityProperties.setProperty("runtime.log.logsystem.class", - "org.apache.velocity.runtime.log.NullLogChute"); - - String address; - try { - address = config.getString(Keys.WEB_ADDRESS, InetAddress.getLocalHost().getHostAddress()); - } catch (UnknownHostException e) { - address = "localhost"; - } - - String webUrl = URIUtil.newURI("http", address, config.getInteger(Keys.WEB_PORT), "", ""); - webUrl = Context.getConfig().getString("web.url", webUrl); - velocityProperties.setProperty("web.url", webUrl); - - velocityEngine = new VelocityEngine(); - velocityEngine.init(velocityProperties); - } - - public static void init(IdentityManager testIdentityManager, MediaManager testMediaManager) { - config = new Config(); - objectMapper = new ObjectMapper(); - objectMapper.registerModule(new JSR353Module()); - client = ClientBuilder.newClient().register(new ObjectMapperContextResolver()); - identityManager = testIdentityManager; - mediaManager = testMediaManager; - } - - public static <T extends BaseModel> BaseObjectManager<T> getManager(Class<T> clazz) { - if (clazz.equals(Device.class)) { - return (BaseObjectManager<T>) deviceManager; - } else if (clazz.equals(Group.class)) { - return (BaseObjectManager<T>) groupsManager; - } else if (clazz.equals(User.class)) { - return (BaseObjectManager<T>) usersManager; - } else if (clazz.equals(Calendar.class)) { - return (BaseObjectManager<T>) calendarManager; - } else if (clazz.equals(Attribute.class)) { - return (BaseObjectManager<T>) attributesManager; - } else if (clazz.equals(Geofence.class)) { - return (BaseObjectManager<T>) geofenceManager; - } else if (clazz.equals(Driver.class)) { - return (BaseObjectManager<T>) driversManager; - } else if (clazz.equals(Command.class)) { - return (BaseObjectManager<T>) commandsManager; - } else if (clazz.equals(Maintenance.class)) { - return (BaseObjectManager<T>) maintenancesManager; - } else if (clazz.equals(Notification.class)) { - return (BaseObjectManager<T>) notificationManager; - } else if (clazz.equals(Order.class)) { - return (BaseObjectManager<T>) orderManager; - } - return null; - } - -} diff --git a/src/main/java/org/traccar/ExtendedObjectDecoder.java b/src/main/java/org/traccar/ExtendedObjectDecoder.java index 46720da52..cddddcd80 100644 --- a/src/main/java/org/traccar/ExtendedObjectDecoder.java +++ b/src/main/java/org/traccar/ExtendedObjectDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 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. @@ -21,18 +21,40 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.util.ReferenceCountUtil; +import org.traccar.config.Config; import org.traccar.config.Keys; +import org.traccar.handler.AcknowledgementHandler; import org.traccar.helper.DataConverter; import org.traccar.model.Position; +import jakarta.inject.Inject; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; import java.util.Collection; +import java.util.List; public abstract class ExtendedObjectDecoder extends ChannelInboundHandlerAdapter { + private Config config; + + public Config getConfig() { + return config; + } + + @Inject + public void setConfig(Config config) { + this.config = config; + init(); + } + + /** + * Method called when config is initialized. + */ + protected void init() { + } + private void saveOriginal(Object decodedMessage, Object originalMessage) { - if (Context.getConfig().getBoolean(Keys.DATABASE_SAVE_ORIGINAL) && decodedMessage instanceof Position) { + if (getConfig().getBoolean(Keys.DATABASE_SAVE_ORIGINAL) && decodedMessage instanceof Position) { Position position = (Position) decodedMessage; if (originalMessage instanceof ByteBuf) { ByteBuf buf = (ByteBuf) originalMessage; @@ -48,6 +70,7 @@ public abstract class ExtendedObjectDecoder extends ChannelInboundHandlerAdapter public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { NetworkMessage networkMessage = (NetworkMessage) msg; Object originalMessage = networkMessage.getMessage(); + ctx.writeAndFlush(new AcknowledgementHandler.EventReceived()); try { Object decodedMessage = decode(ctx.channel(), networkMessage.getRemoteAddress(), originalMessage); onMessageEvent(ctx.channel(), networkMessage.getRemoteAddress(), originalMessage, decodedMessage); @@ -56,14 +79,19 @@ public abstract class ExtendedObjectDecoder extends ChannelInboundHandlerAdapter } if (decodedMessage != null) { if (decodedMessage instanceof Collection) { - for (Object o : (Collection) decodedMessage) { + var collection = (Collection) decodedMessage; + ctx.writeAndFlush(new AcknowledgementHandler.EventDecoded(collection)); + for (Object o : collection) { saveOriginal(o, originalMessage); ctx.fireChannelRead(o); } } else { + ctx.writeAndFlush(new AcknowledgementHandler.EventDecoded(List.of(decodedMessage))); saveOriginal(decodedMessage, originalMessage); ctx.fireChannelRead(decodedMessage); } + } else { + ctx.writeAndFlush(new AcknowledgementHandler.EventDecoded(List.of())); } } finally { ReferenceCountUtil.release(originalMessage); diff --git a/src/main/java/org/traccar/LifecycleObject.java b/src/main/java/org/traccar/LifecycleObject.java index 7af21d528..fe0dc698a 100644 --- a/src/main/java/org/traccar/LifecycleObject.java +++ b/src/main/java/org/traccar/LifecycleObject.java @@ -17,5 +17,5 @@ package org.traccar; public interface LifecycleObject { void start() throws Exception; - void stop(); + void stop() throws Exception; } diff --git a/src/main/java/org/traccar/Main.java b/src/main/java/org/traccar/Main.java index 6daf72bb4..e34fbb72a 100644 --- a/src/main/java/org/traccar/Main.java +++ b/src/main/java/org/traccar/Main.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,13 @@ import com.google.inject.Guice; import com.google.inject.Injector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.traccar.broadcast.BroadcastService; +import org.traccar.helper.model.DeviceUtil; +import org.traccar.schedule.ScheduleManager; +import org.traccar.storage.DatabaseModule; +import org.traccar.storage.Storage; +import org.traccar.web.WebModule; +import org.traccar.web.WebServer; import java.io.File; import java.lang.management.ManagementFactory; @@ -26,10 +33,10 @@ import java.lang.management.MemoryMXBean; import java.lang.management.OperatingSystemMXBean; import java.lang.management.RuntimeMXBean; import java.nio.charset.Charset; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; import java.util.Locale; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; public final class Main { @@ -110,31 +117,36 @@ public final class Main { public static void run(String configFile) { try { - Context.init(configFile); - injector = Guice.createInjector(new MainModule()); + injector = Guice.createInjector(new MainModule(configFile), new DatabaseModule(), new WebModule()); logSystemInfo(); LOGGER.info("Version: " + Main.class.getPackage().getImplementationVersion()); LOGGER.info("Starting server..."); - List<LifecycleObject> services = new LinkedList<>(); - services.add(Context.getServerManager()); - if (Context.getWebServer() != null) { - services.add(Context.getWebServer()); + if (injector.getInstance(BroadcastService.class).singleInstance()) { + DeviceUtil.resetStatus(injector.getInstance(Storage.class)); } - services.add(Context.getScheduleManager()); - for (LifecycleObject service : services) { + var services = Stream.of( + ServerManager.class, WebServer.class, ScheduleManager.class, BroadcastService.class) + .map(injector::getInstance) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + for (var service : services) { service.start(); } Thread.setDefaultUncaughtExceptionHandler((t, e) -> LOGGER.error("Thread exception", e)); Runtime.getRuntime().addShutdownHook(new Thread(() -> { - LOGGER.info("Shutting down server..."); - - Collections.reverse(services); - for (LifecycleObject service : services) { - service.stop(); + LOGGER.info("Stopping server..."); + + for (var service : services) { + try { + service.stop(); + } catch (Exception e) { + throw new RuntimeException(e); + } } })); } catch (Exception e) { diff --git a/src/main/java/org/traccar/MainEventHandler.java b/src/main/java/org/traccar/MainEventHandler.java index a3f6f4105..fb0171d63 100644 --- a/src/main/java/org/traccar/MainEventHandler.java +++ b/src/main/java/org/traccar/MainEventHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package org.traccar; import io.netty.channel.Channel; +import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.socket.DatagramChannel; @@ -23,17 +24,32 @@ import io.netty.handler.codec.http.HttpRequestDecoder; import io.netty.handler.timeout.IdleStateEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +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; +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; import java.util.Set; +@Singleton +@ChannelHandler.Sharable public class MainEventHandler extends ChannelInboundHandlerAdapter { private static final Logger LOGGER = LoggerFactory.getLogger(MainEventHandler.class); @@ -41,13 +57,24 @@ public class MainEventHandler extends ChannelInboundHandlerAdapter { private final Set<String> connectionlessProtocols = new HashSet<>(); private final Set<String> logAttributes = new LinkedHashSet<>(); - public MainEventHandler() { - String connectionlessProtocolList = Context.getConfig().getString(Keys.STATUS_IGNORE_OFFLINE); + 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) { + 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("[, ]"))); } - logAttributes.addAll(Arrays.asList( - Context.getConfig().getString(Keys.LOGGER_ATTRIBUTES).split("[, ]"))); + logAttributes.addAll(Arrays.asList(config.getString(Keys.LOGGER_ATTRIBUTES).split("[, ]"))); } @Override @@ -55,17 +82,27 @@ public class MainEventHandler extends ChannelInboundHandlerAdapter { if (msg instanceof Position) { Position position = (Position) msg; + Device device = cacheManager.getObject(Device.class, position.getDeviceId()); + try { - Context.getDeviceManager().updateLatestPosition(position); + 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); } - String uniqueId = Context.getIdentityManager().getById(position.getDeviceId()).getUniqueId(); - StringBuilder builder = new StringBuilder(); - builder.append(formatChannel(ctx.channel())).append(" "); - builder.append("id: ").append(uniqueId); + builder.append("[").append(NetworkUtil.session(ctx.channel())).append("] "); + builder.append("id: ").append(device.getUniqueId()); for (String attribute : logAttributes) { switch (attribute) { case "time": @@ -108,31 +145,27 @@ public class MainEventHandler extends ChannelInboundHandlerAdapter { } LOGGER.info(builder.toString()); - Main.getInjector().getInstance(StatisticsManager.class) - .registerMessageStored(position.getDeviceId(), position.getProtocol()); - } - } + statisticsManager.registerMessageStored(position.getDeviceId(), position.getProtocol()); - private static String formatChannel(Channel channel) { - return String.format("[%s]", channel.id().asShortText()); + ctx.writeAndFlush(new AcknowledgementHandler.EventHandled(position)); + } } @Override public void channelActive(ChannelHandlerContext ctx) { if (!(ctx.channel() instanceof DatagramChannel)) { - LOGGER.info(formatChannel(ctx.channel()) + " connected"); + LOGGER.info("[{}] connected", NetworkUtil.session(ctx.channel())); } } @Override public void channelInactive(ChannelHandlerContext ctx) { - LOGGER.info(formatChannel(ctx.channel()) + " disconnected"); + LOGGER.info("[{}] disconnected", NetworkUtil.session(ctx.channel())); closeChannel(ctx.channel()); - if (BasePipelineFactory.getHandler(ctx.pipeline(), HttpRequestDecoder.class) == null - && !connectionlessProtocols.contains(ctx.pipeline().get(BaseProtocolDecoder.class).getProtocolName())) { - Context.getConnectionManager().removeActiveDevice(ctx.channel()); - } + boolean supportsOffline = BasePipelineFactory.getHandler(ctx.pipeline(), HttpRequestDecoder.class) == null + && !connectionlessProtocols.contains(ctx.pipeline().get(BaseProtocolDecoder.class).getProtocolName()); + connectionManager.deviceDisconnected(ctx.channel(), supportsOffline); } @Override @@ -140,14 +173,14 @@ public class MainEventHandler extends ChannelInboundHandlerAdapter { while (cause.getCause() != null && cause.getCause() != cause) { cause = cause.getCause(); } - LOGGER.warn(formatChannel(ctx.channel()) + " error", cause); + LOGGER.info("[{}] error", NetworkUtil.session(ctx.channel()), cause); closeChannel(ctx.channel()); } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { if (evt instanceof IdleStateEvent) { - LOGGER.info(formatChannel(ctx.channel()) + " timed out"); + LOGGER.info("[{}] timed out", NetworkUtil.session(ctx.channel())); closeChannel(ctx.channel()); } } diff --git a/src/main/java/org/traccar/MainModule.java b/src/main/java/org/traccar/MainModule.java index 842f7e3ce..6ed240d2c 100644 --- a/src/main/java/org/traccar/MainModule.java +++ b/src/main/java/org/traccar/MainModule.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2018 - 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. @@ -16,20 +16,36 @@ package org.traccar; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsonp.JSONPModule; import com.google.inject.AbstractModule; +import com.google.inject.Injector; import com.google.inject.Provides; -import com.google.inject.Singleton; +import com.google.inject.Scopes; +import com.google.inject.name.Names; +import io.netty.util.HashedWheelTimer; +import io.netty.util.Timer; +import org.apache.velocity.app.VelocityEngine; +import org.traccar.broadcast.BroadcastService; +import org.traccar.broadcast.MulticastBroadcastService; +import org.traccar.broadcast.RedisBroadcastService; +import org.traccar.broadcast.NullBroadcastService; import org.traccar.config.Config; import org.traccar.config.Keys; -import org.traccar.database.AttributesManager; -import org.traccar.database.CalendarManager; -import org.traccar.database.ConnectionManager; -import org.traccar.database.DataManager; -import org.traccar.database.DeviceManager; -import org.traccar.database.GeofenceManager; -import org.traccar.database.IdentityManager; -import org.traccar.database.MaintenancesManager; +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.geocoder.AddressFormat; import org.traccar.geocoder.BanGeocoder; import org.traccar.geocoder.BingMapsGeocoder; @@ -41,128 +57,146 @@ import org.traccar.geocoder.Geocoder; import org.traccar.geocoder.GisgraphyGeocoder; import org.traccar.geocoder.GoogleGeocoder; import org.traccar.geocoder.HereGeocoder; +import org.traccar.geocoder.LocationIqGeocoder; import org.traccar.geocoder.MapQuestGeocoder; import org.traccar.geocoder.MapTilerGeocoder; +import org.traccar.geocoder.MapboxGeocoder; import org.traccar.geocoder.MapmyIndiaGeocoder; import org.traccar.geocoder.NominatimGeocoder; import org.traccar.geocoder.OpenCageGeocoder; import org.traccar.geocoder.PositionStackGeocoder; +import org.traccar.geocoder.TestGeocoder; import org.traccar.geocoder.TomTomGeocoder; -import org.traccar.geocoder.MapboxGeocoder; 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.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.GeolocationHandler; -import org.traccar.handler.HemisphereHandler; -import org.traccar.handler.MotionHandler; -import org.traccar.handler.RemoteAddressHandler; import org.traccar.handler.SpeedLimitHandler; -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.FuelDropEventHandler; -import org.traccar.handler.events.GeofenceEventHandler; -import org.traccar.handler.events.IgnitionEventHandler; -import org.traccar.handler.events.MaintenanceEventHandler; -import org.traccar.handler.events.MotionEventHandler; -import org.traccar.handler.events.OverspeedEventHandler; -import org.traccar.reports.model.TripsConfig; - -import javax.annotation.Nullable; -import javax.ws.rs.client.Client; -import io.netty.util.Timer; +import org.traccar.helper.ObjectMapperContextResolver; +import org.traccar.helper.SanitizerModule; +import org.traccar.helper.WebHelper; +import org.traccar.mail.LogMailManager; +import org.traccar.mail.MailManager; +import org.traccar.mail.SmtpMailManager; +import org.traccar.session.cache.CacheManager; +import org.traccar.sms.HttpSmsClient; +import org.traccar.sms.SmsManager; +import org.traccar.sms.SnsSmsClient; import org.traccar.speedlimit.OverpassSpeedLimitProvider; import org.traccar.speedlimit.SpeedLimitProvider; +import org.traccar.storage.DatabaseStorage; +import org.traccar.storage.MemoryStorage; import org.traccar.storage.Storage; +import org.traccar.web.WebServer; +import org.traccar.api.security.LoginService; + +import jakarta.annotation.Nullable; +import jakarta.inject.Singleton; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.http.HttpClient; +import java.util.Properties; public class MainModule extends AbstractModule { - @Provides - public static ObjectMapper provideObjectMapper() { - return Context.getObjectMapper(); - } + private final String configFile; - @Provides - public static Config provideConfig() { - return Context.getConfig(); - } - - @Provides - public static Storage provideStorage() { - return Context.getDataManager().getStorage(); - } - - @Provides - public static DataManager provideDataManager() { - return Context.getDataManager(); - } - - @Provides - public static IdentityManager provideIdentityManager() { - return Context.getIdentityManager(); + public MainModule(String configFile) { + this.configFile = configFile; } - @Provides - public static ConnectionManager provideConnectionManager() { - return Context.getConnectionManager(); + @Override + protected void configure() { + bindConstant().annotatedWith(Names.named("configFile")).to(configFile); + bind(Config.class).asEagerSingleton(); + bind(Timer.class).to(HashedWheelTimer.class).in(Scopes.SINGLETON); } + @Singleton @Provides - public static Client provideClient() { - return Context.getClient(); + public static Storage provideStorage(Injector injector, Config config) { + if (config.getBoolean(Keys.DATABASE_MEMORY)) { + return injector.getInstance(MemoryStorage.class); + } else { + return injector.getInstance(DatabaseStorage.class); + } } + @Singleton @Provides - public static TripsConfig provideTripsConfig() { - return Context.getTripsConfig(); + public static ObjectMapper provideObjectMapper(Config config) { + ObjectMapper objectMapper = new ObjectMapper(); + if (config.getBoolean(Keys.WEB_SANITIZE)) { + objectMapper.registerModule(new SanitizerModule()); + } + objectMapper.registerModule(new JSONPModule()); + objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + return objectMapper; } + @Singleton @Provides - public static DeviceManager provideDeviceManager() { - return Context.getDeviceManager(); + public static Client provideClient(ObjectMapperContextResolver objectMapperContextResolver) { + return ClientBuilder.newClient().register(objectMapperContextResolver); } + @Singleton @Provides - public static GeofenceManager provideGeofenceManager() { - return Context.getGeofenceManager(); + public static SmsManager provideSmsManager(Config config, Client client) { + if (config.hasKey(Keys.SMS_HTTP_URL)) { + return new HttpSmsClient(config, client); + } else if (config.hasKey(Keys.SMS_AWS_REGION)) { + return new SnsSmsClient(config); + } + return null; } + @Singleton @Provides - public static CalendarManager provideCalendarManager() { - return Context.getCalendarManager(); + public static MailManager provideMailManager(Config config, StatisticsManager statisticsManager) { + if (config.getBoolean(Keys.MAIL_DEBUG)) { + return new LogMailManager(); + } else { + return new SmtpMailManager(config, statisticsManager); + } } + @Singleton @Provides - public static AttributesManager provideAttributesManager() { - return Context.getAttributesManager(); + public static LdapProvider provideLdapProvider(Config config) { + if (config.hasKey(Keys.LDAP_URL)) { + return new LdapProvider(config); + } + return null; } + @Singleton @Provides - public static MaintenancesManager provideMaintenancesManager() { - return Context.getMaintenancesManager(); + public static OpenIdProvider provideOpenIDProvider( + Config config, LoginService loginService, ObjectMapper objectMapper + ) throws InterruptedException, IOException, URISyntaxException { + if (config.hasKey(Keys.OPENID_CLIENT_ID)) { + return new OpenIdProvider(config, loginService, HttpClient.newHttpClient(), objectMapper); + } + return null; } - @Singleton @Provides - public static StatisticsManager provideStatisticsManager( - Config config, DataManager dataManager, Client client, ObjectMapper objectMapper) { - return new StatisticsManager(config, dataManager, client, objectMapper); + public static WebServer provideWebServer(Injector injector, Config config) { + if (config.hasKey(Keys.WEB_PORT)) { + return new WebServer(injector, config); + } + return null; } @Singleton @Provides - public static Geocoder provideGeocoder(Config config) { + public static Geocoder provideGeocoder(Config config, Client client, StatisticsManager statisticsManager) { if (config.getBoolean(Keys.GEOCODER_ENABLE)) { String type = config.getString(Keys.GEOCODER_TYPE, "google"); String url = config.getString(Keys.GEOCODER_URL); @@ -173,62 +207,88 @@ public class MainModule extends AbstractModule { AddressFormat addressFormat = formatString != null ? new AddressFormat(formatString) : new AddressFormat(); int cacheSize = config.getInteger(Keys.GEOCODER_CACHE_SIZE); + Geocoder geocoder; switch (type) { + case "test": + geocoder = new TestGeocoder(); + break; case "nominatim": - return new NominatimGeocoder(url, key, language, cacheSize, addressFormat); + geocoder = new NominatimGeocoder(client, url, key, language, cacheSize, addressFormat); + break; + case "locationiq": + geocoder = new LocationIqGeocoder(client, url, key, language, cacheSize, addressFormat); + break; case "gisgraphy": - return new GisgraphyGeocoder(url, cacheSize, addressFormat); + geocoder = new GisgraphyGeocoder(client, url, cacheSize, addressFormat); + break; case "mapquest": - return new MapQuestGeocoder(url, key, cacheSize, addressFormat); + geocoder = new MapQuestGeocoder(client, url, key, cacheSize, addressFormat); + break; case "opencage": - return new OpenCageGeocoder(url, key, language, cacheSize, addressFormat); + geocoder = new OpenCageGeocoder(client, url, key, language, cacheSize, addressFormat); + break; case "bingmaps": - return new BingMapsGeocoder(url, key, cacheSize, addressFormat); + geocoder = new BingMapsGeocoder(client, url, key, cacheSize, addressFormat); + break; case "factual": - return new FactualGeocoder(url, key, cacheSize, addressFormat); + geocoder = new FactualGeocoder(client, url, key, cacheSize, addressFormat); + break; case "geocodefarm": - return new GeocodeFarmGeocoder(key, language, cacheSize, addressFormat); + geocoder = new GeocodeFarmGeocoder(client, key, language, cacheSize, addressFormat); + break; case "geocodexyz": - return new GeocodeXyzGeocoder(key, cacheSize, addressFormat); + geocoder = new GeocodeXyzGeocoder(client, key, cacheSize, addressFormat); + break; case "ban": - return new BanGeocoder(cacheSize, addressFormat); + geocoder = new BanGeocoder(client, cacheSize, addressFormat); + break; case "here": - return new HereGeocoder(url, id, key, language, cacheSize, addressFormat); + geocoder = new HereGeocoder(client, url, id, key, language, cacheSize, addressFormat); + break; case "mapmyindia": - return new MapmyIndiaGeocoder(url, key, cacheSize, addressFormat); + geocoder = new MapmyIndiaGeocoder(client, url, key, cacheSize, addressFormat); + break; case "tomtom": - return new TomTomGeocoder(url, key, cacheSize, addressFormat); + geocoder = new TomTomGeocoder(client, url, key, cacheSize, addressFormat); + break; case "positionstack": - return new PositionStackGeocoder(key, cacheSize, addressFormat); + geocoder = new PositionStackGeocoder(client, key, cacheSize, addressFormat); + break; case "mapbox": - return new MapboxGeocoder(key, cacheSize, addressFormat); + geocoder = new MapboxGeocoder(client, key, cacheSize, addressFormat); + break; case "maptiler": - return new MapTilerGeocoder(key, cacheSize, addressFormat); + geocoder = new MapTilerGeocoder(client, key, cacheSize, addressFormat); + break; case "geoapify": - return new GeoapifyGeocoder(key, language, cacheSize, addressFormat); + geocoder = new GeoapifyGeocoder(client, key, language, cacheSize, addressFormat); + break; default: - return new GoogleGeocoder(key, language, cacheSize, addressFormat); + geocoder = new GoogleGeocoder(client, key, language, cacheSize, addressFormat); + break; } + geocoder.setStatisticsManager(statisticsManager); + return geocoder; } return null; } @Singleton @Provides - public static GeolocationProvider provideGeolocationProvider(Config config) { + public static GeolocationProvider provideGeolocationProvider(Config config, Client client) { if (config.getBoolean(Keys.GEOLOCATION_ENABLE)) { String type = config.getString(Keys.GEOLOCATION_TYPE, "mozilla"); String url = config.getString(Keys.GEOLOCATION_URL); String key = config.getString(Keys.GEOLOCATION_KEY); switch (type) { case "google": - return new GoogleGeolocationProvider(key); + return new GoogleGeolocationProvider(client, key); case "opencellid": - return new OpenCellIdGeolocationProvider(url, key); + return new OpenCellIdGeolocationProvider(client, url, key); case "unwired": - return new UnwiredGeolocationProvider(url, key); + return new UnwiredGeolocationProvider(client, url, key); default: - return new MozillaGeolocationProvider(key); + return new MozillaGeolocationProvider(client, key); } } return null; @@ -236,14 +296,14 @@ public class MainModule extends AbstractModule { @Singleton @Provides - public static SpeedLimitProvider provideSpeedLimitProvider(Config config) { + public static SpeedLimitProvider provideSpeedLimitProvider(Config config, Client client) { if (config.getBoolean(Keys.SPEED_LIMIT_ENABLE)) { String type = config.getString(Keys.SPEED_LIMIT_TYPE, "overpass"); String url = config.getString(Keys.SPEED_LIMIT_URL); switch (type) { case "overpass": default: - return new OverpassSpeedLimitProvider(url); + return new OverpassSpeedLimitProvider(client, url); } } return null; @@ -251,53 +311,11 @@ public class MainModule extends AbstractModule { @Singleton @Provides - public static DistanceHandler provideDistanceHandler(Config config, IdentityManager identityManager) { - return new DistanceHandler(config, identityManager); - } - - @Singleton - @Provides - public static FilterHandler provideFilterHandler(Config config) { - if (config.getBoolean(Keys.FILTER_ENABLE)) { - return new FilterHandler(config); - } - return null; - } - - @Singleton - @Provides - public static HemisphereHandler provideHemisphereHandler(Config config) { - if (config.hasKey(Keys.LOCATION_LATITUDE_HEMISPHERE) || config.hasKey(Keys.LOCATION_LONGITUDE_HEMISPHERE)) { - return new HemisphereHandler(config); - } - return null; - } - - @Singleton - @Provides - public static RemoteAddressHandler provideRemoteAddressHandler(Config config) { - if (config.getBoolean(Keys.PROCESSING_REMOTE_ADDRESS_ENABLE)) { - return new RemoteAddressHandler(); - } - return null; - } - - @Singleton - @Provides - public static WebDataHandler provideWebDataHandler( - Config config, IdentityManager identityManager, ObjectMapper objectMapper, Client client) { - if (config.hasKey(Keys.FORWARD_URL)) { - return new WebDataHandler(config, identityManager, objectMapper, client); - } - return null; - } - - @Singleton - @Provides public static GeolocationHandler provideGeolocationHandler( - Config config, @Nullable GeolocationProvider geolocationProvider, StatisticsManager statisticsManager) { + Config config, @Nullable GeolocationProvider geolocationProvider, CacheManager cacheManager, + StatisticsManager statisticsManager) { if (geolocationProvider != null) { - return new GeolocationHandler(config, geolocationProvider, statisticsManager); + return new GeolocationHandler(config, geolocationProvider, cacheManager, statisticsManager); } return null; } @@ -305,9 +323,9 @@ public class MainModule extends AbstractModule { @Singleton @Provides public static GeocoderHandler provideGeocoderHandler( - Config config, @Nullable Geocoder geocoder, IdentityManager identityManager) { + Config config, @Nullable Geocoder geocoder, CacheManager cacheManager) { if (geocoder != null) { - return new GeocoderHandler(config, geocoder, identityManager); + return new GeocoderHandler(config, geocoder, cacheManager); } return null; } @@ -323,130 +341,72 @@ public class MainModule extends AbstractModule { @Singleton @Provides - public static MotionHandler provideMotionHandler(TripsConfig tripsConfig) { - return new MotionHandler(tripsConfig.getSpeedThreshold()); - } - - @Singleton - @Provides - public static EngineHoursHandler provideEngineHoursHandler(Config config, IdentityManager identityManager) { - if (config.getBoolean(Keys.PROCESSING_ENGINE_HOURS_ENABLE)) { - return new EngineHoursHandler(identityManager); - } - return null; - } - - @Singleton - @Provides - public static CopyAttributesHandler provideCopyAttributesHandler(Config config, IdentityManager identityManager) { - if (config.getBoolean(Keys.PROCESSING_COPY_ATTRIBUTES_ENABLE)) { - return new CopyAttributesHandler(identityManager); - } - return null; - } - - @Singleton - @Provides - public static ComputedAttributesHandler provideComputedAttributesHandler( - Config config, IdentityManager identityManager, AttributesManager attributesManager) { - if (config.getBoolean(Keys.PROCESSING_COMPUTED_ATTRIBUTES_ENABLE)) { - return new ComputedAttributesHandler(config, identityManager, attributesManager); + public static BroadcastService provideBroadcastService( + Config config, ObjectMapper objectMapper) throws IOException { + if (config.hasKey(Keys.BROADCAST_TYPE)) { + switch (config.getString(Keys.BROADCAST_TYPE)) { + case "multicast": + return new MulticastBroadcastService(config, objectMapper); + case "redis": + return new RedisBroadcastService(config, objectMapper); + default: + break; + } } - return null; + return new NullBroadcastService(); } @Singleton @Provides - public static TimeHandler provideTimeHandler(Config config) { - if (config.hasKey(Keys.TIME_OVERRIDE)) { - return new TimeHandler(config); + public static EventForwarder provideEventForwarder(Config config, Client client, ObjectMapper objectMapper) { + if (config.hasKey(Keys.EVENT_FORWARD_URL)) { + String forwardType = config.getString(Keys.EVENT_FORWARD_TYPE); + switch (forwardType) { + case "amqp": + return new EventForwarderAmqp(config, objectMapper); + case "kafka": + return new EventForwarderKafka(config, objectMapper); + case "mqtt": + return new EventForwarderMqtt(config, objectMapper); + case "json": + default: + return new EventForwarderJson(config, client); + } } return null; } @Singleton @Provides - public static DefaultDataHandler provideDefaultDataHandler(@Nullable DataManager dataManager) { - if (dataManager != null) { - return new DefaultDataHandler(dataManager); + public static PositionForwarder providePositionForwarder(Config config, Client client, ObjectMapper objectMapper) { + if (config.hasKey(Keys.FORWARD_URL)) { + switch (config.getString(Keys.FORWARD_TYPE)) { + case "json": + return new PositionForwarderJson(config, client, objectMapper); + case "amqp": + return new PositionForwarderAmqp(config, objectMapper); + case "kafka": + return new PositionForwarderKafka(config, objectMapper); + case "redis": + return new PositionForwarderRedis(config, objectMapper); + case "url": + default: + return new PositionForwarderUrl(config, client, objectMapper); + } } return null; } @Singleton @Provides - public static CommandResultEventHandler provideCommandResultEventHandler() { - return new CommandResultEventHandler(); - } + public static VelocityEngine provideVelocityEngine(Config config) { + Properties properties = new Properties(); + properties.setProperty("resource.loader.file.path", config.getString(Keys.TEMPLATES_ROOT) + "/"); + properties.setProperty("web.url", WebHelper.retrieveWebUrl(config)); - @Singleton - @Provides - public static OverspeedEventHandler provideOverspeedEventHandler( - Config config, DeviceManager deviceManager, GeofenceManager geofenceManager) { - return new OverspeedEventHandler(config, deviceManager, geofenceManager); - } - - @Singleton - @Provides - public static BehaviorEventHandler provideBehaviorEventHandler(Config config, IdentityManager identityManager) { - return new BehaviorEventHandler(config, identityManager); - } - - @Singleton - @Provides - public static FuelDropEventHandler provideFuelDropEventHandler(IdentityManager identityManager) { - return new FuelDropEventHandler(identityManager); - } - - @Singleton - @Provides - public static MotionEventHandler provideMotionEventHandler( - IdentityManager identityManager, DeviceManager deviceManager, TripsConfig tripsConfig) { - return new MotionEventHandler(identityManager, deviceManager, tripsConfig); - } - - @Singleton - @Provides - public static GeofenceEventHandler provideGeofenceEventHandler( - IdentityManager identityManager, GeofenceManager geofenceManager, CalendarManager calendarManager, - ConnectionManager connectionManager) { - return new GeofenceEventHandler(identityManager, geofenceManager, calendarManager, connectionManager); - } - - @Singleton - @Provides - public static AlertEventHandler provideAlertEventHandler(Config config, IdentityManager identityManager) { - return new AlertEventHandler(config, identityManager); - } - - @Singleton - @Provides - public static IgnitionEventHandler provideIgnitionEventHandler(IdentityManager identityManager) { - return new IgnitionEventHandler(identityManager); - } - - @Singleton - @Provides - public static MaintenanceEventHandler provideMaintenanceEventHandler( - IdentityManager identityManager, MaintenancesManager maintenancesManager) { - return new MaintenanceEventHandler(identityManager, maintenancesManager); - } - - @Singleton - @Provides - public static DriverEventHandler provideDriverEventHandler(IdentityManager identityManager) { - return new DriverEventHandler(identityManager); - } - - @Singleton - @Provides - public static Timer provideTimer() { - return GlobalTimer.getTimer(); - } - - @Override - protected void configure() { - binder().requireExplicitBindings(); + VelocityEngine velocityEngine = new VelocityEngine(); + velocityEngine.init(properties); + return velocityEngine; } } diff --git a/src/main/java/org/traccar/PositionForwardingHandler.java b/src/main/java/org/traccar/PositionForwardingHandler.java new file mode 100644 index 000000000..a79b01367 --- /dev/null +++ b/src/main/java/org/traccar/PositionForwardingHandler.java @@ -0,0 +1,141 @@ +/* + * 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; + +import io.netty.channel.ChannelHandler; +import io.netty.util.Timeout; +import io.netty.util.Timer; +import io.netty.util.TimerTask; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.forward.PositionData; +import org.traccar.forward.PositionForwarder; +import org.traccar.forward.ResultHandler; +import org.traccar.model.Device; +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 { + + private static final Logger LOGGER = LoggerFactory.getLogger(PositionForwardingHandler.class); + + private final CacheManager cacheManager; + private final Timer timer; + + private final PositionForwarder positionForwarder; + + private final boolean retryEnabled; + private final int retryDelay; + private final int retryCount; + private final int retryLimit; + + private final AtomicInteger deliveryPending; + + @Inject + public PositionForwardingHandler( + Config config, CacheManager cacheManager, Timer timer, @Nullable PositionForwarder positionForwarder) { + + this.cacheManager = cacheManager; + this.timer = timer; + this.positionForwarder = positionForwarder; + + this.retryEnabled = config.getBoolean(Keys.FORWARD_RETRY_ENABLE); + this.retryDelay = config.getInteger(Keys.FORWARD_RETRY_DELAY); + this.retryCount = config.getInteger(Keys.FORWARD_RETRY_COUNT); + this.retryLimit = config.getInteger(Keys.FORWARD_RETRY_LIMIT); + + this.deliveryPending = new AtomicInteger(); + } + + class AsyncRequestAndCallback implements ResultHandler, TimerTask { + + private final PositionData positionData; + + private int retries = 0; + + AsyncRequestAndCallback(PositionData positionData) { + this.positionData = positionData; + deliveryPending.incrementAndGet(); + } + + private void send() { + positionForwarder.forward(positionData, this); + } + + private void retry(Throwable throwable) { + boolean scheduled = false; + try { + if (retryEnabled && deliveryPending.get() <= retryLimit && retries < retryCount) { + schedule(); + scheduled = true; + } + } finally { + int pending = scheduled ? deliveryPending.get() : deliveryPending.decrementAndGet(); + LOGGER.warn("Position forwarding failed: " + pending + " pending", throwable); + } + } + + private void schedule() { + timer.newTimeout(this, retryDelay * (long) Math.pow(2, retries++), TimeUnit.MILLISECONDS); + } + + @Override + public void onResult(boolean success, Throwable throwable) { + if (success) { + deliveryPending.decrementAndGet(); + } else { + retry(throwable); + } + } + + @Override + public void run(Timeout timeout) { + boolean sent = false; + try { + if (!timeout.isCancelled()) { + send(); + sent = true; + } + } finally { + if (!sent) { + deliveryPending.decrementAndGet(); + } + } + } + } + + @Override + protected Position handlePosition(Position position) { + if (positionForwarder != null) { + PositionData positionData = new PositionData(); + positionData.setPosition(position); + positionData.setDevice(cacheManager.getObject(Device.class, position.getDeviceId())); + new AsyncRequestAndCallback(positionData).send(); + } + return position; + } + +} diff --git a/src/main/java/org/traccar/ServerManager.java b/src/main/java/org/traccar/ServerManager.java index 15faf9f2b..e91be50a2 100644 --- a/src/main/java/org/traccar/ServerManager.java +++ b/src/main/java/org/traccar/ServerManager.java @@ -15,20 +15,28 @@ */ package org.traccar; +import com.google.inject.Injector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.helper.ClassScanner; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import java.io.IOException; import java.net.BindException; import java.net.ConnectException; import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +@Singleton public class ServerManager implements LifecycleObject { private static final Logger LOGGER = LoggerFactory.getLogger(ServerManager.class); @@ -36,12 +44,21 @@ public class ServerManager implements LifecycleObject { private final List<TrackerConnector> connectorList = new LinkedList<>(); private final Map<String, BaseProtocol> protocolList = new ConcurrentHashMap<>(); - public ServerManager() throws IOException, URISyntaxException, ReflectiveOperationException { + @Inject + public ServerManager( + Injector injector, Config config) throws IOException, URISyntaxException, ReflectiveOperationException { + Set<String> enabledProtocols = null; + if (config.hasKey(Keys.PROTOCOLS_ENABLE)) { + enabledProtocols = new HashSet<>(Arrays.asList(config.getString(Keys.PROTOCOLS_ENABLE).split("[, ]"))); + } for (Class<?> protocolClass : ClassScanner.findSubclasses(BaseProtocol.class, "org.traccar.protocol")) { - if (Context.getConfig().hasKey(Keys.PROTOCOL_PORT.withPrefix(BaseProtocol.nameFromClass(protocolClass)))) { - BaseProtocol protocol = (BaseProtocol) protocolClass.getDeclaredConstructor().newInstance(); - connectorList.addAll(protocol.getConnectorList()); - protocolList.put(protocol.getName(), protocol); + String protocolName = BaseProtocol.nameFromClass(protocolClass); + if (enabledProtocols == null || enabledProtocols.contains(protocolName)) { + if (config.hasKey(Keys.PROTOCOL_PORT.withPrefix(protocolName))) { + BaseProtocol protocol = (BaseProtocol) injector.getInstance(protocolClass); + connectorList.addAll(protocol.getConnectorList()); + protocolList.put(protocol.getName(), protocol); + } } } } @@ -64,11 +81,14 @@ public class ServerManager implements LifecycleObject { } @Override - public void stop() { - for (TrackerConnector connector: connectorList) { - connector.stop(); + public void stop() throws Exception { + try { + for (TrackerConnector connector : connectorList) { + connector.stop(); + } + } finally { + GlobalTimer.release(); } - GlobalTimer.release(); } } diff --git a/src/main/java/org/traccar/TrackerClient.java b/src/main/java/org/traccar/TrackerClient.java index dda02f909..2d6b227da 100644 --- a/src/main/java/org/traccar/TrackerClient.java +++ b/src/main/java/org/traccar/TrackerClient.java @@ -23,6 +23,7 @@ import io.netty.handler.ssl.SslHandler; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; import io.netty.util.concurrent.GlobalEventExecutor; +import org.traccar.config.Config; import org.traccar.config.Keys; import javax.net.ssl.SSLContext; @@ -52,15 +53,14 @@ public abstract class TrackerClient implements TrackerConnector { return secure; } - public TrackerClient(String protocol) { + public TrackerClient(Config config, String protocol) { + secure = config.getBoolean(Keys.PROTOCOL_SSL.withPrefix(protocol)); + interval = config.getLong(Keys.PROTOCOL_INTERVAL.withPrefix(protocol)); + address = config.getString(Keys.PROTOCOL_ADDRESS.withPrefix(protocol)); + port = config.getInteger(Keys.PROTOCOL_PORT.withPrefix(protocol), secure ? 443 : 80); + devices = config.getString(Keys.PROTOCOL_DEVICES.withPrefix(protocol)).split("[, ]"); - secure = Context.getConfig().getBoolean(Keys.PROTOCOL_SSL.withPrefix(protocol)); - interval = Context.getConfig().getLong(Keys.PROTOCOL_INTERVAL.withPrefix(protocol)); - address = Context.getConfig().getString(Keys.PROTOCOL_ADDRESS.withPrefix(protocol)); - port = Context.getConfig().getInteger(Keys.PROTOCOL_PORT.withPrefix(protocol), secure ? 443 : 80); - devices = Context.getConfig().getString(Keys.PROTOCOL_DEVICES.withPrefix(protocol)).split("[, ]"); - - BasePipelineFactory pipelineFactory = new BasePipelineFactory(this, protocol) { + BasePipelineFactory pipelineFactory = new BasePipelineFactory(this, config, protocol) { @Override protected void addTransportHandlers(PipelineBuilder pipeline) { try { @@ -77,7 +77,7 @@ public abstract class TrackerClient implements TrackerConnector { @Override protected void addProtocolHandlers(PipelineBuilder pipeline) { try { - TrackerClient.this.addProtocolHandlers(pipeline); + TrackerClient.this.addProtocolHandlers(pipeline, config); } catch (Exception e) { throw new RuntimeException(e); } @@ -90,7 +90,7 @@ public abstract class TrackerClient implements TrackerConnector { .handler(pipelineFactory); } - protected abstract void addProtocolHandlers(PipelineBuilder pipeline) throws Exception; + protected abstract void addProtocolHandlers(PipelineBuilder pipeline, Config config) throws Exception; public String[] getDevices() { return devices; diff --git a/src/main/java/org/traccar/TrackerServer.java b/src/main/java/org/traccar/TrackerServer.java index 8e2fce616..0e0837cfb 100644 --- a/src/main/java/org/traccar/TrackerServer.java +++ b/src/main/java/org/traccar/TrackerServer.java @@ -25,6 +25,7 @@ import io.netty.channel.socket.nio.NioDatagramChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.ssl.SslHandler; import io.netty.util.concurrent.GlobalEventExecutor; +import org.traccar.config.Config; import org.traccar.config.Keys; import javax.net.ssl.SSLContext; @@ -54,14 +55,12 @@ public abstract class TrackerServer implements TrackerConnector { return secure; } - public TrackerServer(boolean datagram, String protocol) { - this.datagram = datagram; - - secure = Context.getConfig().getBoolean(Keys.PROTOCOL_SSL.withPrefix(protocol)); - address = Context.getConfig().getString(Keys.PROTOCOL_ADDRESS.withPrefix(protocol)); - port = Context.getConfig().getInteger(Keys.PROTOCOL_PORT.withPrefix(protocol)); + public TrackerServer(Config config, String protocol, boolean datagram) { + secure = config.getBoolean(Keys.PROTOCOL_SSL.withPrefix(protocol)); + address = config.getString(Keys.PROTOCOL_ADDRESS.withPrefix(protocol)); + port = config.getInteger(Keys.PROTOCOL_PORT.withPrefix(protocol)); - BasePipelineFactory pipelineFactory = new BasePipelineFactory(this, protocol) { + BasePipelineFactory pipelineFactory = new BasePipelineFactory(this, config, protocol) { @Override protected void addTransportHandlers(PipelineBuilder pipeline) { try { @@ -76,28 +75,25 @@ public abstract class TrackerServer implements TrackerConnector { @Override protected void addProtocolHandlers(PipelineBuilder pipeline) { - TrackerServer.this.addProtocolHandlers(pipeline); + TrackerServer.this.addProtocolHandlers(pipeline, config); } }; + this.datagram = datagram; if (datagram) { - bootstrap = new Bootstrap() .group(EventLoopGroupFactory.getWorkerGroup()) .channel(NioDatagramChannel.class) .handler(pipelineFactory); - } else { - bootstrap = new ServerBootstrap() .group(EventLoopGroupFactory.getBossGroup(), EventLoopGroupFactory.getWorkerGroup()) .channel(NioServerSocketChannel.class) .childHandler(pipelineFactory); - } } - protected abstract void addProtocolHandlers(PipelineBuilder pipeline); + protected abstract void addProtocolHandlers(PipelineBuilder pipeline, Config config); public int getPort() { return port; diff --git a/src/main/java/org/traccar/WebDataHandler.java b/src/main/java/org/traccar/WebDataHandler.java deleted file mode 100644 index 678096d34..000000000 --- a/src/main/java/org/traccar/WebDataHandler.java +++ /dev/null @@ -1,310 +0,0 @@ -/* - * Copyright 2015 - 2020 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.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.netty.channel.ChannelHandler; -import io.netty.util.Timer; -import io.netty.util.Timeout; -import io.netty.util.TimerTask; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.traccar.config.Config; -import org.traccar.config.Keys; -import org.traccar.database.IdentityManager; -import org.traccar.helper.Checksum; -import org.traccar.model.Device; -import org.traccar.model.Position; -import org.traccar.model.Group; - -import javax.inject.Inject; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.Invocation; -import javax.ws.rs.client.InvocationCallback; -import java.util.HashMap; -import java.util.Map; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.Calendar; -import java.util.Formatter; -import java.util.Locale; -import java.util.TimeZone; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -@ChannelHandler.Sharable -public class WebDataHandler extends BaseDataHandler { - - private static final Logger LOGGER = LoggerFactory.getLogger(WebDataHandler.class); - - private static final String KEY_POSITION = "position"; - private static final String KEY_DEVICE = "device"; - - private final IdentityManager identityManager; - private final ObjectMapper objectMapper; - private final Client client; - - private final String url; - private final String header; - private final boolean json; - private final boolean urlVariables; - - private final boolean retryEnabled; - private final int retryDelay; - private final int retryCount; - private final int retryLimit; - - private final AtomicInteger deliveryPending; - - @Inject - public WebDataHandler( - Config config, IdentityManager identityManager, ObjectMapper objectMapper, Client client) { - - this.identityManager = identityManager; - this.objectMapper = objectMapper; - this.client = client; - this.url = config.getString(Keys.FORWARD_URL); - this.header = config.getString(Keys.FORWARD_HEADER); - this.json = config.getBoolean(Keys.FORWARD_JSON); - this.urlVariables = config.getBoolean(Keys.FORWARD_URL_VARIABLES); - - this.retryEnabled = config.getBoolean(Keys.FORWARD_RETRY_ENABLE); - this.retryDelay = config.getInteger(Keys.FORWARD_RETRY_DELAY, 100); - this.retryCount = config.getInteger(Keys.FORWARD_RETRY_COUNT, 10); - this.retryLimit = config.getInteger(Keys.FORWARD_RETRY_LIMIT, 100); - - this.deliveryPending = new AtomicInteger(0); - } - - private static String formatSentence(Position position) { - - StringBuilder s = new StringBuilder("$GPRMC,"); - - try (Formatter f = new Formatter(s, Locale.ENGLISH)) { - - Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"), Locale.ENGLISH); - calendar.setTimeInMillis(position.getFixTime().getTime()); - - f.format("%1$tH%1$tM%1$tS.%1$tL,A,", calendar); - - double lat = position.getLatitude(); - double lon = position.getLongitude(); - - f.format("%02d%07.4f,%c,", (int) Math.abs(lat), Math.abs(lat) % 1 * 60, lat < 0 ? 'S' : 'N'); - f.format("%03d%07.4f,%c,", (int) Math.abs(lon), Math.abs(lon) % 1 * 60, lon < 0 ? 'W' : 'E'); - - f.format("%.2f,%.2f,", position.getSpeed(), position.getCourse()); - f.format("%1$td%1$tm%1$ty,,", calendar); - } - - s.append(Checksum.nmea(s.substring(1))); - - return s.toString(); - } - - private String calculateStatus(Position position) { - if (position.getAttributes().containsKey(Position.KEY_ALARM)) { - return "0xF841"; // STATUS_PANIC_ON - } else if (position.getSpeed() < 1.0) { - return "0xF020"; // STATUS_LOCATION - } else { - return "0xF11C"; // STATUS_MOTION_MOVING - } - } - - public String formatRequest(Position position) throws UnsupportedEncodingException, JsonProcessingException { - - Device device = identityManager.getById(position.getDeviceId()); - - String request = url - .replace("{name}", URLEncoder.encode(device.getName(), StandardCharsets.UTF_8.name())) - .replace("{uniqueId}", device.getUniqueId()) - .replace("{status}", device.getStatus()) - .replace("{deviceId}", String.valueOf(position.getDeviceId())) - .replace("{protocol}", String.valueOf(position.getProtocol())) - .replace("{deviceTime}", String.valueOf(position.getDeviceTime().getTime())) - .replace("{fixTime}", String.valueOf(position.getFixTime().getTime())) - .replace("{valid}", String.valueOf(position.getValid())) - .replace("{latitude}", String.valueOf(position.getLatitude())) - .replace("{longitude}", String.valueOf(position.getLongitude())) - .replace("{altitude}", String.valueOf(position.getAltitude())) - .replace("{speed}", String.valueOf(position.getSpeed())) - .replace("{course}", String.valueOf(position.getCourse())) - .replace("{accuracy}", String.valueOf(position.getAccuracy())) - .replace("{statusCode}", calculateStatus(position)); - - if (position.getAddress() != null) { - request = request.replace( - "{address}", URLEncoder.encode(position.getAddress(), StandardCharsets.UTF_8.name())); - } - - if (request.contains("{attributes}")) { - String attributes = objectMapper.writeValueAsString(position.getAttributes()); - request = request.replace( - "{attributes}", URLEncoder.encode(attributes, StandardCharsets.UTF_8.name())); - } - - if (request.contains("{gprmc}")) { - request = request.replace("{gprmc}", formatSentence(position)); - } - - if (request.contains("{group}")) { - String deviceGroupName = ""; - if (device.getGroupId() != 0) { - Group group = Context.getGroupsManager().getById(device.getGroupId()); - if (group != null) { - deviceGroupName = group.getName(); - } - } - - request = request.replace("{group}", URLEncoder.encode(deviceGroupName, StandardCharsets.UTF_8.name())); - } - - return request; - } - - class AsyncRequestAndCallback implements InvocationCallback<Response>, TimerTask { - - private int retries = 0; - private Map<String, Object> payload; - private final Invocation.Builder requestBuilder; - private MediaType mediaType = MediaType.APPLICATION_JSON_TYPE; - - AsyncRequestAndCallback(Position position) { - - String formattedUrl; - try { - formattedUrl = json && !urlVariables ? url : formatRequest(position); - } catch (UnsupportedEncodingException | JsonProcessingException e) { - throw new RuntimeException("Forwarding formatting error", e); - } - - requestBuilder = client.target(formattedUrl).request(); - if (header != null && !header.isEmpty()) { - for (String line: header.split("\\r?\\n")) { - String[] values = line.split(":", 2); - String headerName = values[0].trim(); - String headerValue = values[1].trim(); - if (headerName.equals(HttpHeaders.CONTENT_TYPE)) { - mediaType = MediaType.valueOf(headerValue); - } else { - requestBuilder.header(headerName, headerValue); - } - } - } - - if (json) { - payload = prepareJsonPayload(position); - } - - deliveryPending.incrementAndGet(); - } - - private void send() { - LOGGER.debug("Position forwarding initiated"); - if (json) { - try { - Entity<String> entity = Entity.entity(objectMapper.writeValueAsString(payload), mediaType); - requestBuilder.async().post(entity, this); - } catch (JsonProcessingException e) { - throw new RuntimeException("Failed to serialize location to json", e); - } - } else { - requestBuilder.async().get(this); - } - } - - private void retry(Throwable throwable) { - boolean scheduled = false; - try { - if (retryEnabled && deliveryPending.get() <= retryLimit && retries < retryCount) { - schedule(); - scheduled = true; - } - } finally { - int pending = scheduled ? deliveryPending.get() : deliveryPending.decrementAndGet(); - LOGGER.warn("Position forwarding failed: " + pending + " pending", throwable); - } - } - - private void schedule() { - Main.getInjector().getInstance(Timer.class).newTimeout( - this, retryDelay * (long) Math.pow(2, retries++), TimeUnit.MILLISECONDS); - } - - @Override - public void completed(Response response) { - if (response.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL) { - deliveryPending.decrementAndGet(); - LOGGER.debug("Position forwarding succeeded"); - } else { - retry(new RuntimeException("Status code 2xx expected")); - } - } - - @Override - public void failed(Throwable throwable) { - retry(throwable); - } - - @Override - public void run(Timeout timeout) { - boolean sent = false; - try { - if (!timeout.isCancelled()) { - send(); - sent = true; - } - } finally { - if (!sent) { - deliveryPending.decrementAndGet(); - } - } - } - - } - - @Override - protected Position handlePosition(Position position) { - - AsyncRequestAndCallback request = new AsyncRequestAndCallback(position); - request.send(); - - return position; - } - - private Map<String, Object> prepareJsonPayload(Position position) { - - Map<String, Object> data = new HashMap<>(); - Device device = identityManager.getById(position.getDeviceId()); - - data.put(KEY_POSITION, position); - - if (device != null) { - data.put(KEY_DEVICE, device); - } - - return data; - } - -} diff --git a/src/main/java/org/traccar/api/AsyncSocket.java b/src/main/java/org/traccar/api/AsyncSocket.java index b1853822d..5fc4b4412 100644 --- a/src/main/java/org/traccar/api/AsyncSocket.java +++ b/src/main/java/org/traccar/api/AsyncSocket.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2021 Anton Tananaev (anton@traccar.org) + * 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. @@ -16,15 +16,18 @@ package org.traccar.api; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.WebSocketAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.traccar.Context; -import org.traccar.database.ConnectionManager; +import org.traccar.helper.model.PositionUtil; +import org.traccar.session.ConnectionManager; import org.traccar.model.Device; import org.traccar.model.Event; import org.traccar.model.Position; +import org.traccar.storage.Storage; +import org.traccar.storage.StorageException; import java.util.Collection; import java.util.Collections; @@ -39,9 +42,15 @@ public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.U private static final String KEY_POSITIONS = "positions"; private static final String KEY_EVENTS = "events"; + private final ObjectMapper objectMapper; + private final ConnectionManager connectionManager; + private final Storage storage; private final long userId; - public AsyncSocket(long userId) { + public AsyncSocket(ObjectMapper objectMapper, ConnectionManager connectionManager, Storage storage, long userId) { + this.objectMapper = objectMapper; + this.connectionManager = connectionManager; + this.storage = storage; this.userId = userId; } @@ -49,18 +58,21 @@ public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.U public void onWebSocketConnect(Session session) { super.onWebSocketConnect(session); - Map<String, Collection<?>> data = new HashMap<>(); - data.put(KEY_POSITIONS, Context.getDeviceManager().getInitialState(userId)); - sendData(data); - - Context.getConnectionManager().addListener(userId, this); + try { + Map<String, Collection<?>> data = new HashMap<>(); + data.put(KEY_POSITIONS, PositionUtil.getLatestPositions(storage, userId)); + sendData(data); + connectionManager.addListener(userId, this); + } catch (StorageException e) { + throw new RuntimeException(e); + } } @Override public void onWebSocketClose(int statusCode, String reason) { super.onWebSocketClose(statusCode, reason); - Context.getConnectionManager().removeListener(userId, this); + connectionManager.removeListener(userId, this); } @Override @@ -92,7 +104,7 @@ public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.U private void sendData(Map<String, Collection<?>> data) { if (isConnected()) { try { - getRemote().sendString(Context.getObjectMapper().writeValueAsString(data), null); + getRemote().sendString(objectMapper.writeValueAsString(data), null); } catch (JsonProcessingException e) { LOGGER.warn("Socket JSON formatting error", e); } diff --git a/src/main/java/org/traccar/api/AsyncSocketServlet.java b/src/main/java/org/traccar/api/AsyncSocketServlet.java index a964ead10..cd2c1639e 100644 --- a/src/main/java/org/traccar/api/AsyncSocketServlet.java +++ b/src/main/java/org/traccar/api/AsyncSocketServlet.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2021 Anton Tananaev (anton@traccar.org) + * 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. @@ -15,27 +15,48 @@ */ package org.traccar.api; +import com.fasterxml.jackson.databind.ObjectMapper; import org.eclipse.jetty.websocket.server.JettyWebSocketServlet; import org.eclipse.jetty.websocket.server.JettyWebSocketServletFactory; -import org.traccar.Context; import org.traccar.api.resource.SessionResource; +import org.traccar.config.Config; import org.traccar.config.Keys; +import org.traccar.session.ConnectionManager; +import org.traccar.storage.Storage; -import javax.servlet.http.HttpSession; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import jakarta.servlet.http.HttpSession; import java.time.Duration; +@Singleton public class AsyncSocketServlet extends JettyWebSocketServlet { + private final Config config; + private final ObjectMapper objectMapper; + private final ConnectionManager connectionManager; + private final Storage storage; + + @Inject + public AsyncSocketServlet( + Config config, ObjectMapper objectMapper, ConnectionManager connectionManager, Storage storage) { + this.config = config; + this.objectMapper = objectMapper; + this.connectionManager = connectionManager; + this.storage = storage; + } + @Override public void configure(JettyWebSocketServletFactory factory) { - factory.setIdleTimeout(Duration.ofMillis(Context.getConfig().getLong(Keys.WEB_TIMEOUT))); + factory.setIdleTimeout(Duration.ofMillis(config.getLong(Keys.WEB_TIMEOUT))); factory.setCreator((req, resp) -> { if (req.getSession() != null) { - long userId = (Long) ((HttpSession) req.getSession()).getAttribute(SessionResource.USER_ID_KEY); - return new AsyncSocket(userId); - } else { - return null; + Long userId = (Long) ((HttpSession) req.getSession()).getAttribute(SessionResource.USER_ID_KEY); + if (userId != null) { + return new AsyncSocket(objectMapper, connectionManager, storage, userId); + } } + return null; }); } diff --git a/src/main/java/org/traccar/api/BaseObjectResource.java b/src/main/java/org/traccar/api/BaseObjectResource.java index 22756f62a..2aaed2bb5 100644 --- a/src/main/java/org/traccar/api/BaseObjectResource.java +++ b/src/main/java/org/traccar/api/BaseObjectResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org) * Copyright 2017 - 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,70 +16,48 @@ */ package org.traccar.api; -import java.sql.SQLException; -import java.util.Set; - -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.core.Response; - -import org.traccar.Context; -import org.traccar.database.BaseObjectManager; -import org.traccar.database.ExtendedObjectManager; -import org.traccar.database.ManagableObjects; -import org.traccar.database.SimpleObjectManager; +import org.traccar.api.security.ServiceAccountUser; import org.traccar.helper.LogAction; import org.traccar.model.BaseModel; -import org.traccar.model.Calendar; -import org.traccar.model.Command; -import org.traccar.model.Device; import org.traccar.model.Group; -import org.traccar.model.GroupedModel; -import org.traccar.model.ScheduledModel; +import org.traccar.model.Permission; import org.traccar.model.User; +import org.traccar.session.ConnectionManager; +import org.traccar.session.cache.CacheManager; 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.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.core.Response; public abstract class BaseObjectResource<T extends BaseModel> extends BaseResource { - private final Class<T> baseClass; + @Inject + private CacheManager cacheManager; - public BaseObjectResource(Class<T> baseClass) { - this.baseClass = baseClass; - } + @Inject + private ConnectionManager connectionManager; - protected final Class<T> getBaseClass() { - return baseClass; - } + protected final Class<T> baseClass; - protected final Set<Long> getSimpleManagerItems(BaseObjectManager<T> manager, boolean all, long userId) { - Set<Long> result; - if (all) { - if (Context.getPermissionsManager().getUserAdmin(getUserId())) { - result = manager.getAllItems(); - } else { - Context.getPermissionsManager().checkManager(getUserId()); - result = ((ManagableObjects) manager).getManagedItems(getUserId()); - } - } else { - if (userId == 0) { - userId = getUserId(); - } - Context.getPermissionsManager().checkUser(getUserId(), userId); - result = ((ManagableObjects) manager).getUserItems(userId); - } - return result; + public BaseObjectResource(Class<T> baseClass) { + this.baseClass = baseClass; } @Path("{id}") @GET - public Response getSingle(@PathParam("id") long id) throws SQLException { - Context.getPermissionsManager().checkPermission(baseClass, getUserId(), id); - BaseObjectManager<T> manager = Context.getManager(baseClass); - T entity = manager.getById(id); + public Response getSingle(@PathParam("id") long id) throws StorageException { + permissionsService.checkPermission(baseClass, getUserId(), id); + T entity = storage.getObject(baseClass, new Request( + new Columns.All(), new Condition.Equals("id", id))); if (entity != null) { return Response.ok(entity).build(); } else { @@ -89,102 +67,66 @@ public abstract class BaseObjectResource<T extends BaseModel> extends BaseResour @POST public Response add(T entity) throws StorageException { - Context.getPermissionsManager().checkReadonly(getUserId()); - if (baseClass.equals(Device.class)) { - Context.getPermissionsManager().checkDeviceReadonly(getUserId()); - Context.getPermissionsManager().checkDeviceLimit(getUserId()); - } else if (baseClass.equals(Command.class)) { - Context.getPermissionsManager().checkLimitCommands(getUserId()); - } else if (entity instanceof GroupedModel && ((GroupedModel) entity).getGroupId() != 0) { - Context.getPermissionsManager().checkPermission( - Group.class, getUserId(), ((GroupedModel) entity).getGroupId()); - } else if (entity instanceof ScheduledModel && ((ScheduledModel) entity).getCalendarId() != 0) { - Context.getPermissionsManager().checkPermission( - Calendar.class, getUserId(), ((ScheduledModel) entity).getCalendarId()); - } + permissionsService.checkEdit(getUserId(), entity, true); - BaseObjectManager<T> manager = Context.getManager(baseClass); - manager.addItem(entity); + entity.setId(storage.addObject(entity, new Request(new Columns.Exclude("id")))); LogAction.create(getUserId(), entity); - Context.getDataManager().linkObject(User.class, getUserId(), baseClass, entity.getId(), true); - LogAction.link(getUserId(), User.class, getUserId(), baseClass, entity.getId()); - - if (manager instanceof SimpleObjectManager) { - ((SimpleObjectManager<T>) manager).refreshUserItems(); - } else if (baseClass.equals(Group.class) || baseClass.equals(Device.class)) { - Context.getPermissionsManager().refreshDeviceAndGroupPermissions(); - Context.getPermissionsManager().refreshAllExtendedPermissions(); + if (getUserId() != ServiceAccountUser.ID) { + storage.addPermission(new Permission(User.class, getUserId(), baseClass, entity.getId())); + cacheManager.invalidatePermission(true, User.class, getUserId(), baseClass, entity.getId()); + connectionManager.invalidatePermission(true, User.class, getUserId(), baseClass, entity.getId()); + LogAction.link(getUserId(), User.class, getUserId(), baseClass, entity.getId()); } + return Response.ok(entity).build(); } @Path("{id}") @PUT public Response update(T entity) throws StorageException { - Context.getPermissionsManager().checkReadonly(getUserId()); - if (baseClass.equals(Device.class)) { - Context.getPermissionsManager().checkDeviceReadonly(getUserId()); - } else if (baseClass.equals(User.class)) { - User before = Context.getPermissionsManager().getUser(entity.getId()); - Context.getPermissionsManager().checkUserUpdate(getUserId(), before, (User) entity); - } else if (baseClass.equals(Command.class)) { - Context.getPermissionsManager().checkLimitCommands(getUserId()); - } else if (entity instanceof GroupedModel && ((GroupedModel) entity).getGroupId() != 0) { - Context.getPermissionsManager().checkPermission( - Group.class, getUserId(), ((GroupedModel) entity).getGroupId()); - } else if (entity instanceof ScheduledModel && ((ScheduledModel) entity).getCalendarId() != 0) { - Context.getPermissionsManager().checkPermission( - Calendar.class, getUserId(), ((ScheduledModel) entity).getCalendarId()); + permissionsService.checkEdit(getUserId(), entity, false); + permissionsService.checkPermission(baseClass, getUserId(), entity.getId()); + + if (entity instanceof User) { + User before = storage.getObject(User.class, new Request( + new Columns.All(), new Condition.Equals("id", entity.getId()))); + permissionsService.checkUserUpdate(getUserId(), before, (User) entity); + } else if (entity instanceof Group) { + Group group = (Group) entity; + if (group.getId() == group.getGroupId()) { + throw new IllegalArgumentException("Cycle in group hierarchy"); + } } - Context.getPermissionsManager().checkPermission(baseClass, getUserId(), entity.getId()); - Context.getManager(baseClass).updateItem(entity); + storage.updateObject(entity, new Request( + new Columns.Exclude("id"), + new Condition.Equals("id", entity.getId()))); + if (entity instanceof User) { + User user = (User) entity; + if (user.getHashedPassword() != null) { + storage.updateObject(entity, new Request( + new Columns.Include("hashedPassword", "salt"), + new Condition.Equals("id", entity.getId()))); + } + } + cacheManager.updateOrInvalidate(true, entity); LogAction.edit(getUserId(), entity); - if (baseClass.equals(Group.class) || baseClass.equals(Device.class)) { - Context.getPermissionsManager().refreshDeviceAndGroupPermissions(); - Context.getPermissionsManager().refreshAllExtendedPermissions(); - } return Response.ok(entity).build(); } @Path("{id}") @DELETE public Response remove(@PathParam("id") long id) throws StorageException { - Context.getPermissionsManager().checkReadonly(getUserId()); - if (baseClass.equals(Device.class)) { - Context.getPermissionsManager().checkDeviceReadonly(getUserId()); - } else if (baseClass.equals(Command.class)) { - Context.getPermissionsManager().checkLimitCommands(getUserId()); - } - Context.getPermissionsManager().checkPermission(baseClass, getUserId(), id); + permissionsService.checkEdit(getUserId(), baseClass, false); + permissionsService.checkPermission(baseClass, getUserId(), id); + + storage.removeObject(baseClass, new Request(new Condition.Equals("id", id))); + cacheManager.invalidate(baseClass, id); - BaseObjectManager<T> manager = Context.getManager(baseClass); - manager.removeItem(id); LogAction.remove(getUserId(), baseClass, id); - if (manager instanceof SimpleObjectManager) { - ((SimpleObjectManager<T>) manager).refreshUserItems(); - if (manager instanceof ExtendedObjectManager) { - ((ExtendedObjectManager<T>) manager).refreshExtendedPermissions(); - } - } - if (baseClass.equals(Group.class) || baseClass.equals(Device.class) || baseClass.equals(User.class)) { - if (baseClass.equals(Group.class)) { - Context.getGroupsManager().refreshItems(); - Context.getDeviceManager().updateDeviceCache(true); - } - Context.getPermissionsManager().refreshDeviceAndGroupPermissions(); - if (baseClass.equals(User.class)) { - Context.getPermissionsManager().refreshAllUsersPermissions(); - } else { - Context.getPermissionsManager().refreshAllExtendedPermissions(); - } - } else if (baseClass.equals(Calendar.class)) { - Context.getGeofenceManager().refreshItems(); - Context.getNotificationManager().refreshItems(); - } return Response.noContent().build(); } diff --git a/src/main/java/org/traccar/api/BaseResource.java b/src/main/java/org/traccar/api/BaseResource.java index 6dff8c8c3..f20b8c4c2 100644 --- a/src/main/java/org/traccar/api/BaseResource.java +++ b/src/main/java/org/traccar/api/BaseResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org) + * 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. @@ -15,16 +15,25 @@ */ package org.traccar.api; +import org.traccar.api.security.PermissionsService; import org.traccar.api.security.UserPrincipal; +import org.traccar.storage.Storage; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.SecurityContext; +import jakarta.inject.Inject; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.SecurityContext; public class BaseResource { @Context private SecurityContext securityContext; + @Inject + protected Storage storage; + + @Inject + protected PermissionsService permissionsService; + protected long getUserId() { UserPrincipal principal = (UserPrincipal) securityContext.getUserPrincipal(); if (principal != null) { @@ -32,4 +41,5 @@ public class BaseResource { } return 0; } + } diff --git a/src/main/java/org/traccar/api/CorsResponseFilter.java b/src/main/java/org/traccar/api/CorsResponseFilter.java index 91aea5718..a380eb41d 100644 --- a/src/main/java/org/traccar/api/CorsResponseFilter.java +++ b/src/main/java/org/traccar/api/CorsResponseFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org) + * 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. @@ -16,16 +16,26 @@ package org.traccar.api; import io.netty.handler.codec.http.HttpHeaderNames; -import org.traccar.Context; +import org.traccar.config.Config; import org.traccar.config.Keys; -import javax.ws.rs.container.ContainerRequestContext; -import javax.ws.rs.container.ContainerResponseContext; -import javax.ws.rs.container.ContainerResponseFilter; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import jakarta.ws.rs.container.ContainerRequestContext; +import jakarta.ws.rs.container.ContainerResponseContext; +import jakarta.ws.rs.container.ContainerResponseFilter; import java.io.IOException; +@Singleton public class CorsResponseFilter implements ContainerResponseFilter { + private final String allowed; + + @Inject + public CorsResponseFilter(Config config) { + allowed = config.getString(Keys.WEB_ORIGIN); + } + private static final String ORIGIN_ALL = "*"; private static final String HEADERS_ALL = "origin, content-type, accept, authorization"; private static final String METHODS_ALL = "GET, POST, PUT, DELETE, OPTIONS"; @@ -46,8 +56,6 @@ public class CorsResponseFilter implements ContainerResponseFilter { if (!response.getHeaders().containsKey(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN.toString())) { String origin = request.getHeaderString(HttpHeaderNames.ORIGIN.toString()); - String allowed = Context.getConfig().getString(Keys.WEB_ORIGIN); - if (origin == null) { response.getHeaders().add(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN.toString(), ORIGIN_ALL); } else if (allowed == null || allowed.equals(ORIGIN_ALL) || allowed.contains(origin)) { diff --git a/src/main/java/org/traccar/api/DateParameterConverterProvider.java b/src/main/java/org/traccar/api/DateParameterConverterProvider.java index f07ece984..4858fcbfd 100644 --- a/src/main/java/org/traccar/api/DateParameterConverterProvider.java +++ b/src/main/java/org/traccar/api/DateParameterConverterProvider.java @@ -17,8 +17,8 @@ package org.traccar.api; import org.traccar.helper.DateUtil; -import javax.ws.rs.ext.ParamConverter; -import javax.ws.rs.ext.ParamConverterProvider; +import jakarta.ws.rs.ext.ParamConverter; +import jakarta.ws.rs.ext.ParamConverterProvider; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.Date; diff --git a/src/main/java/org/traccar/api/ExtendedObjectResource.java b/src/main/java/org/traccar/api/ExtendedObjectResource.java index 9e554217e..348ebe38a 100644 --- a/src/main/java/org/traccar/api/ExtendedObjectResource.java +++ b/src/main/java/org/traccar/api/ExtendedObjectResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org) * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,17 +16,19 @@ */ package org.traccar.api; -import java.sql.SQLException; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -import javax.ws.rs.GET; -import javax.ws.rs.QueryParam; - -import org.traccar.Context; -import org.traccar.database.ExtendedObjectManager; import org.traccar.model.BaseModel; +import org.traccar.model.Device; +import org.traccar.model.Group; +import org.traccar.model.User; +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.ws.rs.GET; +import jakarta.ws.rs.QueryParam; +import java.util.Collection; +import java.util.LinkedList; public class ExtendedObjectResource<T extends BaseModel> extends BaseObjectResource<T> { @@ -36,27 +38,34 @@ public class ExtendedObjectResource<T extends BaseModel> extends BaseObjectResou @GET public Collection<T> get( - @QueryParam("all") boolean all, @QueryParam("userId") long userId, @QueryParam("groupId") long groupId, - @QueryParam("deviceId") long deviceId, @QueryParam("refresh") boolean refresh) throws SQLException { - - ExtendedObjectManager<T> manager = (ExtendedObjectManager<T>) Context.getManager(getBaseClass()); - if (refresh) { - manager.refreshItems(); - } + @QueryParam("all") boolean all, @QueryParam("userId") long userId, + @QueryParam("groupId") long groupId, @QueryParam("deviceId") long deviceId) throws StorageException { - Set<Long> result = new HashSet<>(getSimpleManagerItems(manager, all, userId)); + var conditions = new LinkedList<Condition>(); - if (groupId != 0) { - Context.getPermissionsManager().checkGroup(getUserId(), groupId); - result.retainAll(manager.getGroupItems(groupId)); + if (all) { + if (permissionsService.notAdmin(getUserId())) { + conditions.add(new Condition.Permission(User.class, getUserId(), baseClass)); + } + } else { + if (userId == 0) { + conditions.add(new Condition.Permission(User.class, getUserId(), baseClass)); + } else { + permissionsService.checkUser(getUserId(), userId); + conditions.add(new Condition.Permission(User.class, userId, baseClass).excludeGroups()); + } } - if (deviceId != 0) { - Context.getPermissionsManager().checkDevice(getUserId(), deviceId); - result.retainAll(manager.getDeviceItems(deviceId)); + if (groupId > 0) { + permissionsService.checkPermission(Group.class, getUserId(), groupId); + conditions.add(new Condition.Permission(Group.class, groupId, baseClass).excludeGroups()); + } + if (deviceId > 0) { + permissionsService.checkPermission(Device.class, getUserId(), deviceId); + conditions.add(new Condition.Permission(Device.class, deviceId, baseClass).excludeGroups()); } - return manager.getItems(result); + return storage.getObjects(baseClass, new Request(new Columns.All(), Condition.merge(conditions))); } } diff --git a/src/main/java/org/traccar/api/MediaFilter.java b/src/main/java/org/traccar/api/MediaFilter.java index 77731a810..38d13078d 100644 --- a/src/main/java/org/traccar/api/MediaFilter.java +++ b/src/main/java/org/traccar/api/MediaFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2018 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,35 +16,50 @@ */ package org.traccar.api; -import java.io.IOException; -import java.sql.SQLException; - -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; - -import org.traccar.Context; -import org.traccar.Main; +import com.google.inject.Provider; import org.traccar.api.resource.SessionResource; +import org.traccar.api.security.PermissionsService; import org.traccar.database.StatisticsManager; import org.traccar.helper.Log; import org.traccar.model.Device; +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 jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import java.io.IOException; + +@Singleton public class MediaFilter implements Filter { - @Override - public void init(FilterConfig filterConfig) throws ServletException { + private final Storage storage; + private final StatisticsManager statisticsManager; + private final Provider<PermissionsService> permissionsServiceProvider; + + @Inject + public MediaFilter( + Storage storage, StatisticsManager statisticsManager, + Provider<PermissionsService> permissionsServiceProvider) { + this.storage = storage; + this.statisticsManager = statisticsManager; + this.permissionsServiceProvider = permissionsServiceProvider; } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletResponse httpResponse = (HttpServletResponse) response; try { HttpSession session = ((HttpServletRequest) request).getSession(false); @@ -52,8 +67,7 @@ public class MediaFilter implements Filter { if (session != null) { userId = (Long) session.getAttribute(SessionResource.USER_ID_KEY); if (userId != null) { - Context.getPermissionsManager().checkUserEnabled(userId); - Main.getInjector().getInstance(StatisticsManager.class).registerRequest(userId); + statisticsManager.registerRequest(userId); } } if (userId == null) { @@ -64,26 +78,20 @@ public class MediaFilter implements Filter { String path = ((HttpServletRequest) request).getPathInfo(); String[] parts = path != null ? path.split("/") : null; if (parts != null && parts.length >= 2) { - Device device = Context.getDeviceManager().getByUniqueId(parts[1]); + Device device = storage.getObject(Device.class, new Request( + new Columns.All(), new Condition.Equals("uniqueId", parts[1]))); if (device != null) { - Context.getPermissionsManager().checkDevice(userId, device.getId()); + permissionsServiceProvider.get().checkPermission(Device.class, userId, device.getId()); chain.doFilter(request, response); return; } } httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN); - } catch (SecurityException e) { + } catch (SecurityException | StorageException e) { httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN); httpResponse.getWriter().println(Log.exceptionStack(e)); - } catch (SQLException e) { - httpResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST); - httpResponse.getWriter().println(Log.exceptionStack(e)); } } - @Override - public void destroy() { - } - } diff --git a/src/main/java/org/traccar/api/ResourceErrorHandler.java b/src/main/java/org/traccar/api/ResourceErrorHandler.java index 108a8e8cc..387f3db6a 100644 --- a/src/main/java/org/traccar/api/ResourceErrorHandler.java +++ b/src/main/java/org/traccar/api/ResourceErrorHandler.java @@ -17,9 +17,9 @@ package org.traccar.api; import org.traccar.helper.Log; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response; -import javax.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; public class ResourceErrorHandler implements ExceptionMapper<Exception> { diff --git a/src/main/java/org/traccar/api/SimpleObjectResource.java b/src/main/java/org/traccar/api/SimpleObjectResource.java index a7fcae0e7..c9d41b063 100644 --- a/src/main/java/org/traccar/api/SimpleObjectResource.java +++ b/src/main/java/org/traccar/api/SimpleObjectResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org) * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,15 +16,17 @@ */ package org.traccar.api; -import java.sql.SQLException; -import java.util.Collection; - -import javax.ws.rs.GET; -import javax.ws.rs.QueryParam; - -import org.traccar.Context; -import org.traccar.database.BaseObjectManager; import org.traccar.model.BaseModel; +import org.traccar.model.User; +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.ws.rs.GET; +import jakarta.ws.rs.QueryParam; +import java.util.Collection; +import java.util.LinkedList; public class SimpleObjectResource<T extends BaseModel> extends BaseObjectResource<T> { @@ -34,10 +36,24 @@ public class SimpleObjectResource<T extends BaseModel> extends BaseObjectResourc @GET public Collection<T> get( - @QueryParam("all") boolean all, @QueryParam("userId") long userId) throws SQLException { - - BaseObjectManager<T> manager = Context.getManager(getBaseClass()); - return manager.getItems(getSimpleManagerItems(manager, all, userId)); + @QueryParam("all") boolean all, @QueryParam("userId") long userId) throws StorageException { + + var conditions = new LinkedList<Condition>(); + + if (all) { + if (permissionsService.notAdmin(getUserId())) { + conditions.add(new Condition.Permission(User.class, getUserId(), baseClass)); + } + } else { + if (userId == 0) { + userId = getUserId(); + } else { + permissionsService.checkUser(getUserId(), userId); + } + conditions.add(new Condition.Permission(User.class, userId, baseClass)); + } + + return storage.getObjects(baseClass, new Request(new Columns.All(), Condition.merge(conditions))); } } diff --git a/src/main/java/org/traccar/api/resource/AttributeResource.java b/src/main/java/org/traccar/api/resource/AttributeResource.java index d2dc28903..44f0ef452 100644 --- a/src/main/java/org/traccar/api/resource/AttributeResource.java +++ b/src/main/java/org/traccar/api/resource/AttributeResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org) * Copyright 2017 - 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,80 +16,84 @@ */ package org.traccar.api.resource; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; +import jakarta.inject.Inject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; -import org.traccar.Context; import org.traccar.api.ExtendedObjectResource; import org.traccar.model.Attribute; +import org.traccar.model.Device; import org.traccar.model.Position; import org.traccar.handler.ComputedAttributesHandler; import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Request; @Path("attributes/computed") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public class AttributeResource extends ExtendedObjectResource<Attribute> { + @Inject + private ComputedAttributesHandler computedAttributesHandler; + public AttributeResource() { super(Attribute.class); } @POST @Path("test") - public Response test(@QueryParam("deviceId") long deviceId, Attribute entity) { - Context.getPermissionsManager().checkAdmin(getUserId()); - Context.getPermissionsManager().checkDevice(getUserId(), deviceId); - Position last = Context.getIdentityManager().getLastPosition(deviceId); - if (last != null) { - Object result = new ComputedAttributesHandler( - Context.getConfig(), - Context.getIdentityManager(), - Context.getAttributesManager()).computeAttribute(entity, last); - if (result != null) { - switch (entity.getType()) { - case "number": - Number numberValue = (Number) result; - return Response.ok(numberValue).build(); - case "boolean": - Boolean booleanValue = (Boolean) result; - return Response.ok(booleanValue).build(); - default: - return Response.ok(result.toString()).build(); - } - } else { - return Response.noContent().build(); + public Response test(@QueryParam("deviceId") long deviceId, Attribute entity) throws StorageException { + permissionsService.checkAdmin(getUserId()); + permissionsService.checkPermission(Device.class, getUserId(), deviceId); + + Position position = storage.getObject(Position.class, new Request( + new Columns.All(), + new Condition.LatestPositions(deviceId))); + + Object result = computedAttributesHandler.computeAttribute(entity, position); + if (result != null) { + switch (entity.getType()) { + case "number": + Number numberValue = (Number) result; + return Response.ok(numberValue).build(); + case "boolean": + Boolean booleanValue = (Boolean) result; + return Response.ok(booleanValue).build(); + default: + return Response.ok(result.toString()).build(); } } else { - throw new IllegalArgumentException("Device has no last position"); + return Response.noContent().build(); } } @POST public Response add(Attribute entity) throws StorageException { - Context.getPermissionsManager().checkAdmin(getUserId()); + permissionsService.checkAdmin(getUserId()); return super.add(entity); } @Path("{id}") @PUT public Response update(Attribute entity) throws StorageException { - Context.getPermissionsManager().checkAdmin(getUserId()); + permissionsService.checkAdmin(getUserId()); return super.update(entity); } @Path("{id}") @DELETE public Response remove(@PathParam("id") long id) throws StorageException { - Context.getPermissionsManager().checkAdmin(getUserId()); + permissionsService.checkAdmin(getUserId()); return super.remove(id); } diff --git a/src/main/java/org/traccar/api/resource/CalendarResource.java b/src/main/java/org/traccar/api/resource/CalendarResource.java index 9399c34a5..f6c1f3c59 100644 --- a/src/main/java/org/traccar/api/resource/CalendarResource.java +++ b/src/main/java/org/traccar/api/resource/CalendarResource.java @@ -16,10 +16,10 @@ */ package org.traccar.api.resource; -import javax.ws.rs.Consumes; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; import org.traccar.api.SimpleObjectResource; import org.traccar.model.Calendar; diff --git a/src/main/java/org/traccar/api/resource/CommandResource.java b/src/main/java/org/traccar/api/resource/CommandResource.java index a31345246..d50c7ee0c 100644 --- a/src/main/java/org/traccar/api/resource/CommandResource.java +++ b/src/main/java/org/traccar/api/resource/CommandResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org) * Copyright 2016 Gabor Somogyi (gabor.g.somogyi@gmail.com) * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org) * @@ -17,76 +17,153 @@ */ package org.traccar.api.resource; -import org.traccar.Context; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.BaseProtocol; +import org.traccar.ServerManager; import org.traccar.api.ExtendedObjectResource; import org.traccar.database.CommandsManager; +import org.traccar.helper.model.DeviceUtil; import org.traccar.model.Command; +import org.traccar.model.Device; +import org.traccar.model.Group; +import org.traccar.model.Position; +import org.traccar.model.QueuedCommand; import org.traccar.model.Typed; +import org.traccar.model.User; +import org.traccar.model.UserRestrictions; +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.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; @Path("commands") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public class CommandResource extends ExtendedObjectResource<Command> { + private static final Logger LOGGER = LoggerFactory.getLogger(CommandResource.class); + + @Inject + private CommandsManager commandsManager; + + @Inject + private ServerManager serverManager; + public CommandResource() { super(Command.class); } + private BaseProtocol getDeviceProtocol(long deviceId) throws StorageException { + Position position = storage.getObject(Position.class, new Request( + new Columns.All(), new Condition.LatestPositions(deviceId))); + if (position != null) { + return serverManager.getProtocol(position.getProtocol()); + } else { + return null; + } + } + @GET @Path("send") - public Collection<Command> get(@QueryParam("deviceId") long deviceId) { - Context.getPermissionsManager().checkDevice(getUserId(), deviceId); - CommandsManager commandsManager = Context.getCommandsManager(); - Set<Long> result = new HashSet<>(commandsManager.getUserItems(getUserId())); - result.retainAll(commandsManager.getSupportedCommands(deviceId)); - return commandsManager.getItems(result); + public Collection<Command> get(@QueryParam("deviceId") long deviceId) throws StorageException { + permissionsService.checkPermission(Device.class, getUserId(), deviceId); + BaseProtocol protocol = getDeviceProtocol(deviceId); + + var commands = storage.getObjects(baseClass, new Request( + new Columns.All(), + Condition.merge(List.of( + new Condition.Permission(User.class, getUserId(), baseClass), + new Condition.Permission(Device.class, deviceId, baseClass) + )))); + + return commands.stream().filter(command -> { + String type = command.getType(); + if (protocol != null) { + return command.getTextChannel() && protocol.getSupportedTextCommands().contains(type) + || !command.getTextChannel() && protocol.getSupportedDataCommands().contains(type); + } else { + return type.equals(Command.TYPE_CUSTOM); + } + }).collect(Collectors.toList()); } @POST @Path("send") - public Response send(Command entity) throws Exception { - Context.getPermissionsManager().checkReadonly(getUserId()); - long deviceId = entity.getDeviceId(); - long id = entity.getId(); - Context.getPermissionsManager().checkDevice(getUserId(), deviceId); - if (id != 0) { - Context.getPermissionsManager().checkPermission(Command.class, getUserId(), id); - Context.getPermissionsManager().checkUserDeviceCommand(getUserId(), deviceId, id); + public Response send(Command entity, @QueryParam("groupId") long groupId) throws Exception { + if (entity.getId() > 0) { + permissionsService.checkPermission(baseClass, getUserId(), entity.getId()); + long deviceId = entity.getDeviceId(); + entity = storage.getObject(baseClass, new Request( + new Columns.All(), new Condition.Equals("id", entity.getId()))); + entity.setDeviceId(deviceId); } else { - Context.getPermissionsManager().checkLimitCommands(getUserId()); + permissionsService.checkRestriction(getUserId(), UserRestrictions::getLimitCommands); } - if (!Context.getCommandsManager().sendCommand(entity)) { - return Response.accepted(entity).build(); + boolean result = true; + if (groupId > 0) { + permissionsService.checkPermission(Group.class, getUserId(), groupId); + var devices = DeviceUtil.getAccessibleDevices(storage, getUserId(), List.of(), List.of(groupId)); + for (Device device : devices) { + Command command = QueuedCommand.fromCommand(entity).toCommand(); + command.setDeviceId(device.getId()); + result = commandsManager.sendCommand(command) && result; + } + } else { + permissionsService.checkPermission(Device.class, getUserId(), entity.getDeviceId()); + result = commandsManager.sendCommand(entity); } - return Response.ok(entity).build(); + return result ? Response.ok(entity).build() : Response.accepted(entity).build(); } @GET @Path("types") public Collection<Typed> get( @QueryParam("deviceId") long deviceId, - @QueryParam("protocol") String protocol, - @QueryParam("textChannel") boolean textChannel) { + @QueryParam("textChannel") boolean textChannel) throws StorageException { if (deviceId != 0) { - Context.getPermissionsManager().checkDevice(getUserId(), deviceId); - return Context.getCommandsManager().getCommandTypes(deviceId, textChannel); - } else if (protocol != null) { - return Context.getCommandsManager().getCommandTypes(protocol, textChannel); + permissionsService.checkPermission(Device.class, getUserId(), deviceId); + BaseProtocol protocol = getDeviceProtocol(deviceId); + if (protocol != null) { + if (textChannel) { + return protocol.getSupportedTextCommands().stream().map(Typed::new).collect(Collectors.toList()); + } else { + return protocol.getSupportedDataCommands().stream().map(Typed::new).collect(Collectors.toList()); + } + } else { + return Collections.singletonList(new Typed(Command.TYPE_CUSTOM)); + } } else { - return Context.getCommandsManager().getAllCommandTypes(); + List<Typed> result = new ArrayList<>(); + Field[] fields = Command.class.getDeclaredFields(); + for (Field field : fields) { + if (Modifier.isStatic(field.getModifiers()) && field.getName().startsWith("TYPE_")) { + try { + result.add(new Typed(field.get(null).toString())); + } catch (IllegalArgumentException | IllegalAccessException error) { + LOGGER.warn("Get command types error", error); + } + } + } + return result; } } + } diff --git a/src/main/java/org/traccar/api/resource/DeviceResource.java b/src/main/java/org/traccar/api/resource/DeviceResource.java index 9436b59f6..61a70bac0 100644 --- a/src/main/java/org/traccar/api/resource/DeviceResource.java +++ b/src/main/java/org/traccar/api/resource/DeviceResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org) + * 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. @@ -15,34 +15,58 @@ */ package org.traccar.api.resource; -import org.traccar.Context; import org.traccar.api.BaseObjectResource; -import org.traccar.database.DeviceManager; +import org.traccar.broadcast.BroadcastService; +import org.traccar.database.MediaManager; import org.traccar.helper.LogAction; import org.traccar.model.Device; import org.traccar.model.DeviceAccumulators; +import org.traccar.model.Position; +import org.traccar.model.User; +import org.traccar.session.ConnectionManager; +import org.traccar.session.cache.CacheManager; import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Request; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import java.sql.SQLException; +import jakarta.inject.Inject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; import java.util.Collection; -import java.util.HashSet; +import java.util.LinkedList; import java.util.List; -import java.util.Set; @Path("devices") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public class DeviceResource extends BaseObjectResource<Device> { + @Inject + private CacheManager cacheManager; + + @Inject + private ConnectionManager connectionManager; + + @Inject + private BroadcastService broadcastService; + + @Inject + private MediaManager mediaManager; + public DeviceResource() { super(Device.class); } @@ -51,51 +75,112 @@ public class DeviceResource extends BaseObjectResource<Device> { public Collection<Device> get( @QueryParam("all") boolean all, @QueryParam("userId") long userId, @QueryParam("uniqueId") List<String> uniqueIds, - @QueryParam("id") List<Long> deviceIds) throws SQLException { - DeviceManager deviceManager = Context.getDeviceManager(); - Set<Long> result; - if (all) { - if (Context.getPermissionsManager().getUserAdmin(getUserId())) { - result = deviceManager.getAllItems(); - } else { - Context.getPermissionsManager().checkManager(getUserId()); - result = deviceManager.getManagedItems(getUserId()); - } - } else if (uniqueIds.isEmpty() && deviceIds.isEmpty()) { - if (userId == 0) { - userId = getUserId(); - } - Context.getPermissionsManager().checkUser(getUserId(), userId); - if (Context.getPermissionsManager().getUserAdmin(getUserId())) { - result = deviceManager.getAllUserItems(userId); - } else { - result = deviceManager.getUserItems(userId); - } - } else { - result = new HashSet<>(); + @QueryParam("id") List<Long> deviceIds) throws StorageException { + + if (!uniqueIds.isEmpty() || !deviceIds.isEmpty()) { + + List<Device> result = new LinkedList<>(); for (String uniqueId : uniqueIds) { - Device device = deviceManager.getByUniqueId(uniqueId); - Context.getPermissionsManager().checkDevice(getUserId(), device.getId()); - result.add(device.getId()); + result.addAll(storage.getObjects(Device.class, new Request( + new Columns.All(), + new Condition.And( + new Condition.Equals("uniqueId", uniqueId), + new Condition.Permission(User.class, getUserId(), Device.class))))); } for (Long deviceId : deviceIds) { - Context.getPermissionsManager().checkDevice(getUserId(), deviceId); - result.add(deviceId); + result.addAll(storage.getObjects(Device.class, new Request( + new Columns.All(), + new Condition.And( + new Condition.Equals("id", deviceId), + new Condition.Permission(User.class, getUserId(), Device.class))))); } + return result; + + } else { + + var conditions = new LinkedList<Condition>(); + + if (all) { + if (permissionsService.notAdmin(getUserId())) { + conditions.add(new Condition.Permission(User.class, getUserId(), baseClass)); + } + } else { + if (userId == 0) { + conditions.add(new Condition.Permission(User.class, getUserId(), baseClass)); + } else { + permissionsService.checkUser(getUserId(), userId); + conditions.add(new Condition.Permission(User.class, userId, baseClass).excludeGroups()); + } + } + + return storage.getObjects(baseClass, new Request(new Columns.All(), Condition.merge(conditions))); + } - return deviceManager.getItems(result); } @Path("{id}/accumulators") @PUT public Response updateAccumulators(DeviceAccumulators entity) throws StorageException { - if (!Context.getPermissionsManager().getUserAdmin(getUserId())) { - Context.getPermissionsManager().checkManager(getUserId()); - Context.getPermissionsManager().checkPermission(Device.class, getUserId(), entity.getDeviceId()); + if (permissionsService.notAdmin(getUserId())) { + permissionsService.checkManager(getUserId()); + permissionsService.checkPermission(Device.class, getUserId(), entity.getDeviceId()); } - Context.getDeviceManager().resetDeviceAccumulators(entity); + + Position position = storage.getObject(Position.class, new Request( + new Columns.All(), new Condition.LatestPositions(entity.getDeviceId()))); + if (position != null) { + if (entity.getTotalDistance() != null) { + position.getAttributes().put(Position.KEY_TOTAL_DISTANCE, entity.getTotalDistance()); + } + if (entity.getHours() != null) { + position.getAttributes().put(Position.KEY_HOURS, entity.getHours()); + } + position.setId(storage.addObject(position, new Request(new Columns.Exclude("id")))); + + Device device = new Device(); + device.setId(position.getDeviceId()); + device.setPositionId(position.getId()); + storage.updateObject(device, new Request( + new Columns.Include("positionId"), + new Condition.Equals("id", device.getId()))); + + try { + cacheManager.addDevice(position.getDeviceId()); + cacheManager.updatePosition(position); + connectionManager.updatePosition(true, position); + } finally { + cacheManager.removeDevice(position.getDeviceId()); + } + } else { + throw new IllegalArgumentException(); + } + LogAction.resetDeviceAccumulators(getUserId(), entity.getDeviceId()); return Response.noContent().build(); } + @Path("{id}/image") + @POST + @Consumes("image/*") + public Response uploadImage( + @PathParam("id") long deviceId, File file, + @HeaderParam(HttpHeaders.CONTENT_TYPE) String type) throws StorageException, IOException { + + Device device = storage.getObject(Device.class, new Request( + new Columns.All(), + new Condition.And( + new Condition.Equals("id", deviceId), + new Condition.Permission(User.class, getUserId(), Device.class)))); + if (device != null) { + String name = "device"; + String extension = type.substring("image/".length()); + try (var input = new FileInputStream(file); + var output = mediaManager.createFileStream(device.getUniqueId(), name, extension)) { + input.transferTo(output); + } + return Response.ok(name + "." + extension).build(); + } + return Response.status(Response.Status.NOT_FOUND).build(); + } + } diff --git a/src/main/java/org/traccar/api/resource/DriverResource.java b/src/main/java/org/traccar/api/resource/DriverResource.java index 91aa54c5e..19cf74f39 100644 --- a/src/main/java/org/traccar/api/resource/DriverResource.java +++ b/src/main/java/org/traccar/api/resource/DriverResource.java @@ -16,10 +16,10 @@ */ package org.traccar.api.resource; -import javax.ws.rs.Consumes; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; import org.traccar.api.ExtendedObjectResource; import org.traccar.model.Driver; diff --git a/src/main/java/org/traccar/api/resource/EventResource.java b/src/main/java/org/traccar/api/resource/EventResource.java index 354d96e4f..1f20b880d 100644 --- a/src/main/java/org/traccar/api/resource/EventResource.java +++ b/src/main/java/org/traccar/api/resource/EventResource.java @@ -15,21 +15,22 @@ */ package org.traccar.api.resource; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import org.traccar.Context; import org.traccar.api.BaseResource; +import org.traccar.model.Device; import org.traccar.model.Event; -import org.traccar.model.Geofence; -import org.traccar.model.Maintenance; 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.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; @Path("events") @Produces(MediaType.APPLICATION_JSON) @@ -39,17 +40,12 @@ public class EventResource extends BaseResource { @Path("{id}") @GET public Event get(@PathParam("id") long id) throws StorageException { - Event event = Context.getDataManager().getObject(Event.class, id); + Event event = storage.getObject(Event.class, new Request( + new Columns.All(), new Condition.Equals("id", id))); if (event == null) { throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()); } - Context.getPermissionsManager().checkDevice(getUserId(), event.getDeviceId()); - if (event.getGeofenceId() != 0) { - Context.getPermissionsManager().checkPermission(Geofence.class, getUserId(), event.getGeofenceId()); - } - if (event.getMaintenanceId() != 0) { - Context.getPermissionsManager().checkPermission(Maintenance.class, getUserId(), event.getMaintenanceId()); - } + permissionsService.checkPermission(Device.class, getUserId(), event.getDeviceId()); return event; } diff --git a/src/main/java/org/traccar/api/resource/GeofenceResource.java b/src/main/java/org/traccar/api/resource/GeofenceResource.java index 58f2c188c..030690889 100644 --- a/src/main/java/org/traccar/api/resource/GeofenceResource.java +++ b/src/main/java/org/traccar/api/resource/GeofenceResource.java @@ -18,10 +18,10 @@ package org.traccar.api.resource; import org.traccar.api.ExtendedObjectResource; import org.traccar.model.Geofence; -import javax.ws.rs.Consumes; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; @Path("geofences") @Produces(MediaType.APPLICATION_JSON) diff --git a/src/main/java/org/traccar/api/resource/GroupResource.java b/src/main/java/org/traccar/api/resource/GroupResource.java index fcea15d0a..628f8f655 100644 --- a/src/main/java/org/traccar/api/resource/GroupResource.java +++ b/src/main/java/org/traccar/api/resource/GroupResource.java @@ -18,10 +18,10 @@ package org.traccar.api.resource; import org.traccar.api.SimpleObjectResource; import org.traccar.model.Group; -import javax.ws.rs.Consumes; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; @Path("groups") @Produces(MediaType.APPLICATION_JSON) diff --git a/src/main/java/org/traccar/api/resource/MaintenanceResource.java b/src/main/java/org/traccar/api/resource/MaintenanceResource.java index fa1b359ce..12841e497 100644 --- a/src/main/java/org/traccar/api/resource/MaintenanceResource.java +++ b/src/main/java/org/traccar/api/resource/MaintenanceResource.java @@ -16,10 +16,10 @@ */ package org.traccar.api.resource; -import javax.ws.rs.Consumes; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; import org.traccar.api.ExtendedObjectResource; import org.traccar.model.Maintenance; diff --git a/src/main/java/org/traccar/api/resource/NotificationResource.java b/src/main/java/org/traccar/api/resource/NotificationResource.java index 9631a52b7..2a209efb6 100644 --- a/src/main/java/org/traccar/api/resource/NotificationResource.java +++ b/src/main/java/org/traccar/api/resource/NotificationResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 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. @@ -15,30 +15,42 @@ */ package org.traccar.api.resource; -import java.util.Collection; - -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import org.traccar.Context; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.traccar.api.ExtendedObjectResource; import org.traccar.model.Event; import org.traccar.model.Notification; import org.traccar.model.Typed; +import org.traccar.model.User; import org.traccar.notification.MessageException; +import org.traccar.notification.NotificatorManager; +import org.traccar.storage.StorageException; +import jakarta.inject.Inject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; @Path("notifications") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public class NotificationResource extends ExtendedObjectResource<Notification> { + private static final Logger LOGGER = LoggerFactory.getLogger(NotificationResource.class); + + @Inject + private NotificatorManager notificatorManager; + public NotificationResource() { super(Notification.class); } @@ -46,21 +58,32 @@ public class NotificationResource extends ExtendedObjectResource<Notification> { @GET @Path("types") public Collection<Typed> get() { - return Context.getNotificationManager().getAllNotificationTypes(); + List<Typed> types = new LinkedList<>(); + Field[] fields = Event.class.getDeclaredFields(); + for (Field field : fields) { + if (Modifier.isStatic(field.getModifiers()) && field.getName().startsWith("TYPE_")) { + try { + types.add(new Typed(field.get(null).toString())); + } catch (IllegalArgumentException | IllegalAccessException error) { + LOGGER.warn("Get event types error", error); + } + } + } + return types; } @GET @Path("notificators") public Collection<Typed> getNotificators() { - return Context.getNotificatorManager().getAllNotificatorTypes(); + return notificatorManager.getAllNotificatorTypes(); } @POST @Path("test") - public Response testMessage() throws MessageException, InterruptedException { - for (Typed method : Context.getNotificatorManager().getAllNotificatorTypes()) { - Context.getNotificatorManager() - .getNotificator(method.getType()).sendSync(getUserId(), new Event("test", 0), null); + public Response testMessage() throws MessageException, StorageException { + User user = permissionsService.getUser(getUserId()); + for (Typed method : notificatorManager.getAllNotificatorTypes()) { + notificatorManager.getNotificator(method.getType()).send(null, user, new Event("test", 0), null); } return Response.noContent().build(); } @@ -68,8 +91,9 @@ public class NotificationResource extends ExtendedObjectResource<Notification> { @POST @Path("test/{notificator}") public Response testMessage(@PathParam("notificator") String notificator) - throws MessageException, InterruptedException { - Context.getNotificatorManager().getNotificator(notificator).sendSync(getUserId(), new Event("test", 0), null); + throws MessageException, StorageException { + User user = permissionsService.getUser(getUserId()); + notificatorManager.getNotificator(notificator).send(null, user, new Event("test", 0), null); return Response.noContent().build(); } diff --git a/src/main/java/org/traccar/api/resource/OrderResource.java b/src/main/java/org/traccar/api/resource/OrderResource.java index 77608a508..3852b975f 100644 --- a/src/main/java/org/traccar/api/resource/OrderResource.java +++ b/src/main/java/org/traccar/api/resource/OrderResource.java @@ -18,10 +18,10 @@ package org.traccar.api.resource; import org.traccar.api.SimpleObjectResource; import org.traccar.model.Order; -import javax.ws.rs.Consumes; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; @Path("orders") @Produces(MediaType.APPLICATION_JSON) diff --git a/src/main/java/org/traccar/api/resource/PasswordResource.java b/src/main/java/org/traccar/api/resource/PasswordResource.java index 0642ff3cc..029e63a0c 100644 --- a/src/main/java/org/traccar/api/resource/PasswordResource.java +++ b/src/main/java/org/traccar/api/resource/PasswordResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,49 +15,55 @@ */ package org.traccar.api.resource; -import org.apache.velocity.VelocityContext; -import org.traccar.Context; import org.traccar.api.BaseResource; +import org.traccar.api.signature.TokenManager; +import org.traccar.mail.MailManager; import org.traccar.model.User; -import org.traccar.notification.NotificationMessage; import org.traccar.notification.TextTemplateFormatter; import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Request; -import javax.annotation.security.PermitAll; -import javax.mail.MessagingException; -import javax.ws.rs.Consumes; -import javax.ws.rs.FormParam; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import java.util.UUID; +import jakarta.annotation.security.PermitAll; +import jakarta.inject.Inject; +import jakarta.mail.MessagingException; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import java.io.IOException; +import java.security.GeneralSecurityException; @Path("password") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_FORM_URLENCODED) public class PasswordResource extends BaseResource { - private static final String PASSWORD_RESET_TOKEN = "passwordToken"; + @Inject + private MailManager mailManager; + + @Inject + private TokenManager tokenManager; + + @Inject + private TextTemplateFormatter textTemplateFormatter; @Path("reset") @PermitAll @POST - public Response reset(@FormParam("email") String email) throws StorageException, MessagingException { - for (long userId : Context.getUsersManager().getAllItems()) { - User user = Context.getUsersManager().getById(userId); - if (email.equals(user.getEmail())) { - String token = UUID.randomUUID().toString().replaceAll("-", ""); - user.set(PASSWORD_RESET_TOKEN, token); - Context.getUsersManager().updateItem(user); - VelocityContext velocityContext = TextTemplateFormatter.prepareContext(null); - velocityContext.put("token", token); - NotificationMessage fullMessage = - TextTemplateFormatter.formatMessage(velocityContext, "passwordReset", "full"); - Context.getMailManager().sendMessage(userId, fullMessage.getSubject(), fullMessage.getBody()); - break; - } + public Response reset(@FormParam("email") String email) + throws StorageException, MessagingException, GeneralSecurityException, IOException { + + User user = storage.getObject(User.class, new Request( + new Columns.All(), new Condition.Equals("email", email))); + if (user != null) { + var velocityContext = textTemplateFormatter.prepareContext(permissionsService.getServer(), user); + var fullMessage = textTemplateFormatter.formatMessage(velocityContext, "passwordReset", "full"); + mailManager.sendMessage(user, true, fullMessage.getSubject(), fullMessage.getBody()); } return Response.ok().build(); } @@ -66,15 +72,18 @@ public class PasswordResource extends BaseResource { @PermitAll @POST public Response update( - @FormParam("token") String token, @FormParam("password") String password) throws StorageException { - for (long userId : Context.getUsersManager().getAllItems()) { - User user = Context.getUsersManager().getById(userId); - if (token.equals(user.getString(PASSWORD_RESET_TOKEN))) { - user.getAttributes().remove(PASSWORD_RESET_TOKEN); - user.setPassword(password); - Context.getUsersManager().updateItem(user); - return Response.ok().build(); - } + @FormParam("token") String token, @FormParam("password") String password) + throws StorageException, GeneralSecurityException, IOException { + + long userId = tokenManager.verifyToken(token); + User user = storage.getObject(User.class, new Request( + new Columns.All(), new Condition.Equals("id", userId))); + if (user != null) { + user.setPassword(password); + storage.updateObject(user, new Request( + new Columns.Include("hashedPassword", "salt"), + new Condition.Equals("id", userId))); + return Response.ok().build(); } return Response.status(Response.Status.NOT_FOUND).build(); } diff --git a/src/main/java/org/traccar/api/resource/PermissionsResource.java b/src/main/java/org/traccar/api/resource/PermissionsResource.java index 7def38919..e8e4e96eb 100644 --- a/src/main/java/org/traccar/api/resource/PermissionsResource.java +++ b/src/main/java/org/traccar/api/resource/PermissionsResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org) * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,47 +16,40 @@ */ package org.traccar.api.resource; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Set; - -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import org.traccar.Context; import org.traccar.api.BaseResource; import org.traccar.helper.LogAction; -import org.traccar.model.Device; import org.traccar.model.Permission; -import org.traccar.model.User; +import org.traccar.model.UserRestrictions; +import org.traccar.session.cache.CacheManager; import org.traccar.storage.StorageException; +import jakarta.inject.Inject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Set; + @Path("permissions") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public class PermissionsResource extends BaseResource { - private void checkPermission(Permission permission, boolean link) { - if (!link && permission.getOwnerClass().equals(User.class) - && permission.getPropertyClass().equals(Device.class)) { - if (getUserId() != permission.getOwnerId()) { - Context.getPermissionsManager().checkUser(getUserId(), permission.getOwnerId()); - } else { - Context.getPermissionsManager().checkAdmin(getUserId()); - } - } else { - Context.getPermissionsManager().checkPermission( - permission.getOwnerClass(), getUserId(), permission.getOwnerId()); + @Inject + private CacheManager cacheManager; + + private void checkPermission(Permission permission) throws StorageException { + if (permissionsService.notAdmin(getUserId())) { + permissionsService.checkPermission(permission.getOwnerClass(), getUserId(), permission.getOwnerId()); + permissionsService.checkPermission(permission.getPropertyClass(), getUserId(), permission.getPropertyId()); } - Context.getPermissionsManager().checkPermission( - permission.getPropertyClass(), getUserId(), permission.getPropertyId()); } private void checkPermissionTypes(List<LinkedHashMap<String, Long>> entities) { @@ -72,18 +65,19 @@ public class PermissionsResource extends BaseResource { @Path("bulk") @POST public Response add(List<LinkedHashMap<String, Long>> entities) throws StorageException, ClassNotFoundException { - Context.getPermissionsManager().checkReadonly(getUserId()); + permissionsService.checkRestriction(getUserId(), UserRestrictions::getReadonly); checkPermissionTypes(entities); for (LinkedHashMap<String, Long> entity: entities) { Permission permission = new Permission(entity); - checkPermission(permission, true); - Context.getDataManager().linkObject(permission.getOwnerClass(), permission.getOwnerId(), - permission.getPropertyClass(), permission.getPropertyId(), true); - LogAction.link(getUserId(), permission.getOwnerClass(), permission.getOwnerId(), + checkPermission(permission); + storage.addPermission(permission); + cacheManager.invalidatePermission( + true, + permission.getOwnerClass(), permission.getOwnerId(), + permission.getPropertyClass(), permission.getPropertyId()); + LogAction.link(getUserId(), + permission.getOwnerClass(), permission.getOwnerId(), permission.getPropertyClass(), permission.getPropertyId()); - } - if (!entities.isEmpty()) { - Context.getPermissionsManager().refreshPermissions(new Permission(entities.get(0))); } return Response.noContent().build(); } @@ -96,18 +90,19 @@ public class PermissionsResource extends BaseResource { @DELETE @Path("bulk") public Response remove(List<LinkedHashMap<String, Long>> entities) throws StorageException, ClassNotFoundException { - Context.getPermissionsManager().checkReadonly(getUserId()); + permissionsService.checkRestriction(getUserId(), UserRestrictions::getReadonly); checkPermissionTypes(entities); for (LinkedHashMap<String, Long> entity: entities) { Permission permission = new Permission(entity); - checkPermission(permission, false); - Context.getDataManager().linkObject(permission.getOwnerClass(), permission.getOwnerId(), - permission.getPropertyClass(), permission.getPropertyId(), false); - LogAction.unlink(getUserId(), permission.getOwnerClass(), permission.getOwnerId(), + checkPermission(permission); + storage.removePermission(permission); + cacheManager.invalidatePermission( + true, + permission.getOwnerClass(), permission.getOwnerId(), + permission.getPropertyClass(), permission.getPropertyId()); + LogAction.unlink(getUserId(), + permission.getOwnerClass(), permission.getOwnerId(), permission.getPropertyClass(), permission.getPropertyId()); - } - if (!entities.isEmpty()) { - Context.getPermissionsManager().refreshPermissions(new Permission(entities.get(0))); } return Response.noContent().build(); } diff --git a/src/main/java/org/traccar/api/resource/PositionResource.java b/src/main/java/org/traccar/api/resource/PositionResource.java index 511032402..0d783a0fe 100644 --- a/src/main/java/org/traccar/api/resource/PositionResource.java +++ b/src/main/java/org/traccar/api/resource/PositionResource.java @@ -15,52 +15,146 @@ */ package org.traccar.api.resource; -import org.traccar.Context; import org.traccar.api.BaseResource; +import org.traccar.helper.model.PositionUtil; +import org.traccar.model.Device; import org.traccar.model.Position; +import org.traccar.model.UserRestrictions; +import org.traccar.reports.CsvExportProvider; +import org.traccar.reports.GpxExportProvider; +import org.traccar.reports.KmlExportProvider; import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Request; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; +import jakarta.inject.Inject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.StreamingOutput; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Date; import java.util.List; +import java.util.LinkedList; @Path("positions") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public class PositionResource extends BaseResource { + @Inject + private KmlExportProvider kmlExportProvider; + + @Inject + private CsvExportProvider csvExportProvider; + + @Inject + private GpxExportProvider gpxExportProvider; + @GET public Collection<Position> getJson( @QueryParam("deviceId") long deviceId, @QueryParam("id") List<Long> positionIds, @QueryParam("from") Date from, @QueryParam("to") Date to) throws StorageException { if (!positionIds.isEmpty()) { - ArrayList<Position> positions = new ArrayList<>(); - for (Long positionId : positionIds) { - Position position = Context.getDataManager().getObject(Position.class, positionId); - Context.getPermissionsManager().checkDevice(getUserId(), position.getDeviceId()); + var positions = new ArrayList<Position>(); + for (long positionId : positionIds) { + Position position = storage.getObject(Position.class, new Request( + new Columns.All(), new Condition.Equals("id", positionId))); + permissionsService.checkPermission(Device.class, getUserId(), position.getDeviceId()); positions.add(position); } return positions; - } else if (deviceId == 0) { - return Context.getDeviceManager().getInitialState(getUserId()); - } else { - Context.getPermissionsManager().checkDevice(getUserId(), deviceId); + } else if (deviceId > 0) { + permissionsService.checkPermission(Device.class, getUserId(), deviceId); if (from != null && to != null) { - Context.getPermissionsManager().checkDisableReports(getUserId()); - return Context.getDataManager().getPositions(deviceId, from, to); + permissionsService.checkRestriction(getUserId(), UserRestrictions::getDisableReports); + return PositionUtil.getPositions(storage, deviceId, from, to); } else { - return Collections.singleton(Context.getDeviceManager().getLastPosition(deviceId)); + return storage.getObjects(Position.class, new Request( + new Columns.All(), new Condition.LatestPositions(deviceId))); } + } else { + return PositionUtil.getLatestPositions(storage, getUserId()); } } + @DELETE + public Response remove( + @QueryParam("deviceId") long deviceId, + @QueryParam("from") Date from, @QueryParam("to") Date to) throws StorageException { + permissionsService.checkPermission(Device.class, getUserId(), deviceId); + permissionsService.checkRestriction(getUserId(), UserRestrictions::getReadonly); + + var conditions = new LinkedList<Condition>(); + conditions.add(new Condition.Equals("deviceId", deviceId)); + conditions.add(new Condition.Between("fixTime", "from", from, "to", to)); + storage.removeObject(Position.class, new Request(Condition.merge(conditions))); + + return Response.status(Response.Status.NO_CONTENT).build(); + } + + @Path("kml") + @GET + @Produces("application/vnd.google-earth.kml+xml") + public Response getKml( + @QueryParam("deviceId") long deviceId, + @QueryParam("from") Date from, @QueryParam("to") Date to) throws StorageException { + permissionsService.checkPermission(Device.class, getUserId(), deviceId); + StreamingOutput stream = output -> { + try { + kmlExportProvider.generate(output, deviceId, from, to); + } catch (StorageException e) { + throw new WebApplicationException(e); + } + }; + return Response.ok(stream) + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=positions.kml").build(); + } + + @Path("csv") + @GET + @Produces("text/csv") + public Response getCsv( + @QueryParam("deviceId") long deviceId, + @QueryParam("from") Date from, @QueryParam("to") Date to) throws StorageException { + permissionsService.checkPermission(Device.class, getUserId(), deviceId); + StreamingOutput stream = output -> { + try { + csvExportProvider.generate(output, deviceId, from, to); + } catch (StorageException e) { + throw new WebApplicationException(e); + } + }; + return Response.ok(stream) + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=positions.csv").build(); + } + + @Path("gpx") + @GET + @Produces("application/gpx+xml") + public Response getGpx( + @QueryParam("deviceId") long deviceId, + @QueryParam("from") Date from, @QueryParam("to") Date to) throws StorageException { + permissionsService.checkPermission(Device.class, getUserId(), deviceId); + StreamingOutput stream = output -> { + try { + gpxExportProvider.generate(output, deviceId, from, to); + } catch (StorageException e) { + throw new WebApplicationException(e); + } + }; + return Response.ok(stream) + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=positions.gpx").build(); + } + } diff --git a/src/main/java/org/traccar/api/resource/ReportResource.java b/src/main/java/org/traccar/api/resource/ReportResource.java index 03df0d03a..b4882f219 100644 --- a/src/main/java/org/traccar/api/resource/ReportResource.java +++ b/src/main/java/org/traccar/api/resource/ReportResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2016 - 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,208 +16,307 @@ */ package org.traccar.api.resource; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.Collection; -import java.util.Date; -import java.util.List; - -import javax.activation.DataHandler; -import javax.mail.MessagingException; -import javax.mail.internet.MimeBodyPart; -import javax.mail.util.ByteArrayDataSource; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traccar.Context; -import org.traccar.api.BaseResource; +import org.traccar.api.SimpleObjectResource; import org.traccar.helper.LogAction; import org.traccar.model.Event; import org.traccar.model.Position; -import org.traccar.reports.Events; -import org.traccar.reports.Summary; -import org.traccar.reports.Trips; -import org.traccar.reports.model.StopReport; -import org.traccar.reports.model.SummaryReport; -import org.traccar.reports.model.TripReport; -import org.traccar.reports.Route; -import org.traccar.reports.Stops; +import org.traccar.model.Report; +import org.traccar.model.UserRestrictions; +import org.traccar.reports.CombinedReportProvider; +import org.traccar.reports.EventsReportProvider; +import org.traccar.reports.RouteReportProvider; +import org.traccar.reports.StopsReportProvider; +import org.traccar.reports.SummaryReportProvider; +import org.traccar.reports.TripsReportProvider; +import org.traccar.reports.common.ReportExecutor; +import org.traccar.reports.common.ReportMailer; +import org.traccar.reports.model.CombinedReportItem; +import org.traccar.reports.model.StopReportItem; +import org.traccar.reports.model.SummaryReportItem; +import org.traccar.reports.model.TripReportItem; import org.traccar.storage.StorageException; +import jakarta.inject.Inject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.StreamingOutput; +import java.util.Collection; +import java.util.Date; +import java.util.List; + @Path("reports") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) -public class ReportResource extends BaseResource { +public class ReportResource extends SimpleObjectResource<Report> { - private static final Logger LOGGER = LoggerFactory.getLogger(ReportResource.class); + private static final String EXCEL = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; - private static final String XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; - private static final String CONTENT_DISPOSITION_VALUE_XLSX = "attachment; filename=report.xlsx"; + @Inject + private CombinedReportProvider combinedReportProvider; - private interface ReportExecutor { - void execute(ByteArrayOutputStream stream) throws StorageException, IOException; - } + @Inject + private EventsReportProvider eventsReportProvider; - private Response executeReport( - long userId, boolean mail, ReportExecutor executor) throws StorageException, IOException { - final ByteArrayOutputStream stream = new ByteArrayOutputStream(); - if (mail) { - new Thread(() -> { - try { - executor.execute(stream); + @Inject + private RouteReportProvider routeReportProvider; - MimeBodyPart attachment = new MimeBodyPart(); + @Inject + private StopsReportProvider stopsReportProvider; - attachment.setFileName("report.xlsx"); - attachment.setDataHandler(new DataHandler(new ByteArrayDataSource( - stream.toByteArray(), "application/octet-stream"))); + @Inject + private SummaryReportProvider summaryReportProvider; - Context.getMailManager().sendMessage( - userId, "Report", "The report is in the attachment.", attachment); - } catch (StorageException | IOException | MessagingException e) { - LOGGER.warn("Report failed", e); - } - }).start(); + @Inject + private TripsReportProvider tripsReportProvider; + + @Inject + private ReportMailer reportMailer; + + public ReportResource() { + super(Report.class); + } + + private Response executeReport(long userId, boolean mail, ReportExecutor executor) { + if (mail) { + reportMailer.sendAsync(userId, executor); return Response.noContent().build(); } else { - executor.execute(stream); - return Response.ok(stream.toByteArray()) - .header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE_XLSX).build(); + StreamingOutput stream = output -> { + try { + executor.execute(output); + } catch (StorageException e) { + throw new WebApplicationException(e); + } + }; + return Response.ok(stream) + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=report.xlsx").build(); } } + @Path("combined") + @GET + public Collection<CombinedReportItem> getCombined( + @QueryParam("deviceId") List<Long> deviceIds, + @QueryParam("groupId") List<Long> groupIds, + @QueryParam("from") Date from, + @QueryParam("to") Date to) throws StorageException { + permissionsService.checkRestriction(getUserId(), UserRestrictions::getDisableReports); + LogAction.logReport(getUserId(), "combined", from, to, deviceIds, groupIds); + return combinedReportProvider.getObjects(getUserId(), deviceIds, groupIds, from, to); + } + @Path("route") @GET public Collection<Position> getRoute( - @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, - @QueryParam("from") Date from, @QueryParam("to") Date to) throws StorageException { - Context.getPermissionsManager().checkDisableReports(getUserId()); + @QueryParam("deviceId") List<Long> deviceIds, + @QueryParam("groupId") List<Long> groupIds, + @QueryParam("from") Date from, + @QueryParam("to") Date to) throws StorageException { + permissionsService.checkRestriction(getUserId(), UserRestrictions::getDisableReports); LogAction.logReport(getUserId(), "route", from, to, deviceIds, groupIds); - return Route.getObjects(getUserId(), deviceIds, groupIds, from, to); + return routeReportProvider.getObjects(getUserId(), deviceIds, groupIds, from, to); } @Path("route") @GET - @Produces(XLSX) + @Produces(EXCEL) public Response getRouteExcel( - @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, - @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("mail") boolean mail) - throws StorageException, IOException { - Context.getPermissionsManager().checkDisableReports(getUserId()); + @QueryParam("deviceId") List<Long> deviceIds, + @QueryParam("groupId") List<Long> groupIds, + @QueryParam("from") Date from, + @QueryParam("to") Date to, + @QueryParam("mail") boolean mail) throws StorageException { + permissionsService.checkRestriction(getUserId(), UserRestrictions::getDisableReports); return executeReport(getUserId(), mail, stream -> { LogAction.logReport(getUserId(), "route", from, to, deviceIds, groupIds); - Route.getExcel(stream, getUserId(), deviceIds, groupIds, from, to); + routeReportProvider.getExcel(stream, getUserId(), deviceIds, groupIds, from, to); }); } + @Path("route/{type:xlsx|mail}") + @GET + @Produces(EXCEL) + public Response getRouteExcel( + @QueryParam("deviceId") List<Long> deviceIds, + @QueryParam("groupId") final List<Long> groupIds, + @QueryParam("from") Date from, + @QueryParam("to") Date to, + @PathParam("type") String type) throws StorageException { + return getRouteExcel(deviceIds, groupIds, from, to, type.equals("mail")); + } + @Path("events") @GET public Collection<Event> getEvents( - @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, - @QueryParam("type") final List<String> types, - @QueryParam("from") Date from, @QueryParam("to") Date to) throws StorageException { - Context.getPermissionsManager().checkDisableReports(getUserId()); + @QueryParam("deviceId") List<Long> deviceIds, + @QueryParam("groupId") List<Long> groupIds, + @QueryParam("type") List<String> types, + @QueryParam("from") Date from, + @QueryParam("to") Date to) throws StorageException { + permissionsService.checkRestriction(getUserId(), UserRestrictions::getDisableReports); LogAction.logReport(getUserId(), "events", from, to, deviceIds, groupIds); - return Events.getObjects(getUserId(), deviceIds, groupIds, types, from, to); + return eventsReportProvider.getObjects(getUserId(), deviceIds, groupIds, types, from, to); } @Path("events") @GET - @Produces(XLSX) + @Produces(EXCEL) public Response getEventsExcel( - @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, - @QueryParam("type") final List<String> types, - @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("mail") boolean mail) - throws StorageException, IOException { - Context.getPermissionsManager().checkDisableReports(getUserId()); + @QueryParam("deviceId") List<Long> deviceIds, + @QueryParam("groupId") List<Long> groupIds, + @QueryParam("type") List<String> types, + @QueryParam("from") Date from, + @QueryParam("to") Date to, + @QueryParam("mail") boolean mail) throws StorageException { + permissionsService.checkRestriction(getUserId(), UserRestrictions::getDisableReports); return executeReport(getUserId(), mail, stream -> { LogAction.logReport(getUserId(), "events", from, to, deviceIds, groupIds); - Events.getExcel(stream, getUserId(), deviceIds, groupIds, types, from, to); + eventsReportProvider.getExcel(stream, getUserId(), deviceIds, groupIds, types, from, to); }); } + @Path("events/{type:xlsx|mail}") + @GET + @Produces(EXCEL) + public Response getEventsExcel( + @QueryParam("deviceId") List<Long> deviceIds, + @QueryParam("groupId") List<Long> groupIds, + @QueryParam("type") List<String> types, + @QueryParam("from") Date from, + @QueryParam("to") Date to, + @PathParam("type") String type) throws StorageException { + return getEventsExcel(deviceIds, groupIds, types, from, to, type.equals("mail")); + } + @Path("summary") @GET - public Collection<SummaryReport> getSummary( - @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, - @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("daily") boolean daily) - throws StorageException { - Context.getPermissionsManager().checkDisableReports(getUserId()); + public Collection<SummaryReportItem> getSummary( + @QueryParam("deviceId") List<Long> deviceIds, + @QueryParam("groupId") List<Long> groupIds, + @QueryParam("from") Date from, + @QueryParam("to") Date to, + @QueryParam("daily") boolean daily) throws StorageException { + permissionsService.checkRestriction(getUserId(), UserRestrictions::getDisableReports); LogAction.logReport(getUserId(), "summary", from, to, deviceIds, groupIds); - return Summary.getObjects(getUserId(), deviceIds, groupIds, from, to, daily); + return summaryReportProvider.getObjects(getUserId(), deviceIds, groupIds, from, to, daily); } @Path("summary") @GET - @Produces(XLSX) + @Produces(EXCEL) public Response getSummaryExcel( - @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, - @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("daily") boolean daily, - @QueryParam("mail") boolean mail) - throws StorageException, IOException { - Context.getPermissionsManager().checkDisableReports(getUserId()); + @QueryParam("deviceId") List<Long> deviceIds, + @QueryParam("groupId") List<Long> groupIds, + @QueryParam("from") Date from, + @QueryParam("to") Date to, + @QueryParam("daily") boolean daily, + @QueryParam("mail") boolean mail) throws StorageException { + permissionsService.checkRestriction(getUserId(), UserRestrictions::getDisableReports); return executeReport(getUserId(), mail, stream -> { LogAction.logReport(getUserId(), "summary", from, to, deviceIds, groupIds); - Summary.getExcel(stream, getUserId(), deviceIds, groupIds, from, to, daily); + summaryReportProvider.getExcel(stream, getUserId(), deviceIds, groupIds, from, to, daily); }); } + @Path("summary/{type:xlsx|mail}") + @GET + @Produces(EXCEL) + public Response getSummaryExcel( + @QueryParam("deviceId") List<Long> deviceIds, + @QueryParam("groupId") List<Long> groupIds, + @QueryParam("from") Date from, + @QueryParam("to") Date to, + @QueryParam("daily") boolean daily, + @PathParam("type") String type) throws StorageException { + return getSummaryExcel(deviceIds, groupIds, from, to, daily, type.equals("mail")); + } + @Path("trips") @GET - @Produces(MediaType.APPLICATION_JSON) - public Collection<TripReport> getTrips( - @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, - @QueryParam("from") Date from, @QueryParam("to") Date to) throws StorageException { - Context.getPermissionsManager().checkDisableReports(getUserId()); + public Collection<TripReportItem> getTrips( + @QueryParam("deviceId") List<Long> deviceIds, + @QueryParam("groupId") List<Long> groupIds, + @QueryParam("from") Date from, + @QueryParam("to") Date to) throws StorageException { + permissionsService.checkRestriction(getUserId(), UserRestrictions::getDisableReports); LogAction.logReport(getUserId(), "trips", from, to, deviceIds, groupIds); - return Trips.getObjects(getUserId(), deviceIds, groupIds, from, to); + return tripsReportProvider.getObjects(getUserId(), deviceIds, groupIds, from, to); } @Path("trips") @GET - @Produces(XLSX) + @Produces(EXCEL) public Response getTripsExcel( - @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, - @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("mail") boolean mail) - throws StorageException, IOException { - Context.getPermissionsManager().checkDisableReports(getUserId()); + @QueryParam("deviceId") List<Long> deviceIds, + @QueryParam("groupId") List<Long> groupIds, + @QueryParam("from") Date from, + @QueryParam("to") Date to, + @QueryParam("mail") boolean mail) throws StorageException { + permissionsService.checkRestriction(getUserId(), UserRestrictions::getDisableReports); return executeReport(getUserId(), mail, stream -> { LogAction.logReport(getUserId(), "trips", from, to, deviceIds, groupIds); - Trips.getExcel(stream, getUserId(), deviceIds, groupIds, from, to); + tripsReportProvider.getExcel(stream, getUserId(), deviceIds, groupIds, from, to); }); } + @Path("trips/{type:xlsx|mail}") + @GET + @Produces(EXCEL) + public Response getTripsExcel( + @QueryParam("deviceId") List<Long> deviceIds, + @QueryParam("groupId") List<Long> groupIds, + @QueryParam("from") Date from, + @QueryParam("to") Date to, + @PathParam("type") String type) throws StorageException { + return getTripsExcel(deviceIds, groupIds, from, to, type.equals("mail")); + } + @Path("stops") @GET - @Produces(MediaType.APPLICATION_JSON) - public Collection<StopReport> getStops( - @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, - @QueryParam("from") Date from, @QueryParam("to") Date to) throws StorageException { - Context.getPermissionsManager().checkDisableReports(getUserId()); + public Collection<StopReportItem> getStops( + @QueryParam("deviceId") List<Long> deviceIds, + @QueryParam("groupId") List<Long> groupIds, + @QueryParam("from") Date from, + @QueryParam("to") Date to) throws StorageException { + permissionsService.checkRestriction(getUserId(), UserRestrictions::getDisableReports); LogAction.logReport(getUserId(), "stops", from, to, deviceIds, groupIds); - return Stops.getObjects(getUserId(), deviceIds, groupIds, from, to); + return stopsReportProvider.getObjects(getUserId(), deviceIds, groupIds, from, to); } @Path("stops") @GET - @Produces(XLSX) + @Produces(EXCEL) public Response getStopsExcel( - @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, - @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("mail") boolean mail) - throws StorageException, IOException { - Context.getPermissionsManager().checkDisableReports(getUserId()); + @QueryParam("deviceId") List<Long> deviceIds, + @QueryParam("groupId") List<Long> groupIds, + @QueryParam("from") Date from, + @QueryParam("to") Date to, + @QueryParam("mail") boolean mail) throws StorageException { + permissionsService.checkRestriction(getUserId(), UserRestrictions::getDisableReports); return executeReport(getUserId(), mail, stream -> { LogAction.logReport(getUserId(), "stops", from, to, deviceIds, groupIds); - Stops.getExcel(stream, getUserId(), deviceIds, groupIds, from, to); + stopsReportProvider.getExcel(stream, getUserId(), deviceIds, groupIds, from, to); }); } + @Path("stops/{type:xlsx|mail}") + @GET + @Produces(EXCEL) + public Response getStopsExcel( + @QueryParam("deviceId") List<Long> deviceIds, + @QueryParam("groupId") List<Long> groupIds, + @QueryParam("from") Date from, + @QueryParam("to") Date to, + @PathParam("type") String type) throws StorageException { + return getStopsExcel(deviceIds, groupIds, from, to, type.equals("mail")); + } + } diff --git a/src/main/java/org/traccar/api/resource/ServerResource.java b/src/main/java/org/traccar/api/resource/ServerResource.java index 2d17d5e47..8149ec3b8 100644 --- a/src/main/java/org/traccar/api/resource/ServerResource.java +++ b/src/main/java/org/traccar/api/resource/ServerResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 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. @@ -15,25 +15,46 @@ */ package org.traccar.api.resource; -import org.traccar.Context; import org.traccar.api.BaseResource; +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.database.OpenIdProvider; +import org.traccar.geocoder.Geocoder; +import org.traccar.helper.Log; import org.traccar.helper.LogAction; +import org.traccar.helper.model.UserUtil; +import org.traccar.mail.MailManager; import org.traccar.model.Server; -import org.traccar.storage.Storage; +import org.traccar.model.User; +import org.traccar.session.cache.CacheManager; +import org.traccar.sms.SmsManager; import org.traccar.storage.StorageException; import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import javax.annotation.security.PermitAll; -import javax.inject.Inject; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; +import jakarta.annotation.Nullable; +import jakarta.annotation.security.PermitAll; +import jakarta.inject.Inject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collection; +import java.util.TimeZone; @Path("server") @Produces(MediaType.APPLICATION_JSON) @@ -41,18 +62,56 @@ import javax.ws.rs.core.Response; public class ServerResource extends BaseResource { @Inject - private Storage storage; + private Config config; + + @Inject + private CacheManager cacheManager; + + @Inject + private MailManager mailManager; + + @Inject + @Nullable + private SmsManager smsManager; + + @Inject + @Nullable + private OpenIdProvider openIdProvider; + + @Inject + @Nullable + private Geocoder geocoder; @PermitAll @GET public Server get() throws StorageException { - return storage.getObject(Server.class, new Request(new Columns.All())); + Server server = storage.getObject(Server.class, new Request(new Columns.All())); + server.setEmailEnabled(mailManager.getEmailEnabled()); + server.setTextEnabled(smsManager != null); + server.setGeocoderEnabled(geocoder != null); + server.setOpenIdEnabled(openIdProvider != null); + server.setOpenIdForce(openIdProvider != null && openIdProvider.getForce()); + User user = permissionsService.getUser(getUserId()); + if (user != null) { + if (user.getAdministrator()) { + server.setStorageSpace(Log.getStorageSpace()); + } + } else { + server.setNewServer(UserUtil.isEmpty(storage)); + } + if (user != null && user.getAdministrator()) { + server.setStorageSpace(Log.getStorageSpace()); + } + return server; } @PUT public Response update(Server entity) throws StorageException { - Context.getPermissionsManager().checkAdmin(getUserId()); - Context.getPermissionsManager().updateServer(entity); + permissionsService.checkAdmin(getUserId()); + storage.updateObject(entity, new Request( + new Columns.Exclude("id"), + new Condition.Equals("id", entity.getId()))); + cacheManager.updateOrInvalidate(true, entity); LogAction.edit(getUserId(), entity); return Response.ok(entity).build(); } @@ -60,11 +119,36 @@ public class ServerResource extends BaseResource { @Path("geocode") @GET public String geocode(@QueryParam("latitude") double latitude, @QueryParam("longitude") double longitude) { - if (Context.getGeocoder() != null) { - return Context.getGeocoder().getAddress(latitude, longitude, null); + if (geocoder != null) { + return geocoder.getAddress(latitude, longitude, null); } else { throw new RuntimeException("Reverse geocoding is not enabled"); } } + @Path("timezones") + @GET + public Collection<String> timezones() { + return Arrays.asList(TimeZone.getAvailableIDs()); + } + + @Path("file/{path}") + @POST + @Consumes("*/*") + public Response uploadImage(@PathParam("path") String path, File inputFile) throws IOException, StorageException { + permissionsService.checkAdmin(getUserId()); + String root = config.getString(Keys.WEB_OVERRIDE, config.getString(Keys.WEB_PATH)); + + var outputPath = Paths.get(root, path); + var directoryPath = outputPath.getParent(); + if (directoryPath != null) { + Files.createDirectories(directoryPath); + } + + try (var input = new FileInputStream(inputFile); var output = new FileOutputStream(outputPath.toFile())) { + input.transferTo(output); + } + return Response.ok().build(); + } + } diff --git a/src/main/java/org/traccar/api/resource/SessionResource.java b/src/main/java/org/traccar/api/resource/SessionResource.java index 8422e0b49..3e738c15a 100644 --- a/src/main/java/org/traccar/api/resource/SessionResource.java +++ b/src/main/java/org/traccar/api/resource/SessionResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2021 Anton Tananaev (anton@traccar.org) + * 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. @@ -15,32 +15,44 @@ */ package org.traccar.api.resource; -import org.traccar.Context; import org.traccar.api.BaseResource; +import org.traccar.api.security.LoginService; +import org.traccar.api.signature.TokenManager; +import org.traccar.database.OpenIdProvider; import org.traccar.helper.DataConverter; -import org.traccar.helper.ServletHelper; import org.traccar.helper.LogAction; +import org.traccar.helper.WebHelper; import org.traccar.model.User; import org.traccar.storage.StorageException; - -import javax.annotation.security.PermitAll; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.FormParam; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import java.io.UnsupportedEncodingException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Request; + +import com.nimbusds.oauth2.sdk.ParseException; +import jakarta.annotation.Nullable; +import jakarta.annotation.security.PermitAll; +import jakarta.inject.Inject; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import java.io.IOException; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.util.Date; +import java.net.URI; @Path("session") @Produces(MediaType.APPLICATION_JSON) @@ -51,18 +63,28 @@ public class SessionResource extends BaseResource { public static final String USER_COOKIE_KEY = "user"; public static final String PASS_COOKIE_KEY = "password"; - @javax.ws.rs.core.Context + @Inject + private LoginService loginService; + + @Inject + @Nullable + private OpenIdProvider openIdProvider; + + @Inject + private TokenManager tokenManager; + + @Context private HttpServletRequest request; @PermitAll @GET - public User get(@QueryParam("token") String token) throws StorageException, UnsupportedEncodingException { + public User get(@QueryParam("token") String token) throws StorageException, IOException, GeneralSecurityException { if (token != null) { - User user = Context.getUsersManager().getUserByToken(token); + User user = loginService.login(token); if (user != null) { - Context.getPermissionsManager().checkUserEnabled(user.getId()); request.getSession().setAttribute(USER_ID_KEY, user.getId()); + LogAction.login(user.getId(), WebHelper.retrieveRemoteAddress(request)); return user; } } @@ -76,54 +98,91 @@ public class SessionResource extends BaseResource { for (Cookie cookie : cookies) { if (cookie.getName().equals(USER_COOKIE_KEY)) { byte[] emailBytes = DataConverter.parseBase64( - URLDecoder.decode(cookie.getValue(), StandardCharsets.US_ASCII.name())); + URLDecoder.decode(cookie.getValue(), StandardCharsets.US_ASCII)); email = new String(emailBytes, StandardCharsets.UTF_8); } else if (cookie.getName().equals(PASS_COOKIE_KEY)) { byte[] passwordBytes = DataConverter.parseBase64( - URLDecoder.decode(cookie.getValue(), StandardCharsets.US_ASCII.name())); + URLDecoder.decode(cookie.getValue(), StandardCharsets.US_ASCII)); password = new String(passwordBytes, StandardCharsets.UTF_8); } } } if (email != null && password != null) { - User user = Context.getPermissionsManager().login(email, password); + User user = loginService.login(email, password); if (user != null) { - Context.getPermissionsManager().checkUserEnabled(user.getId()); request.getSession().setAttribute(USER_ID_KEY, user.getId()); + LogAction.login(user.getId(), WebHelper.retrieveRemoteAddress(request)); return user; } } } else { - Context.getPermissionsManager().checkUserEnabled(userId); - return Context.getPermissionsManager().getUser(userId); + User user = permissionsService.getUser(userId); + if (user != null) { + return user; + } } throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()); } + @Path("{id}") + @GET + public User get(@PathParam("id") long userId) throws StorageException { + permissionsService.checkUser(getUserId(), userId); + User user = storage.getObject(User.class, new Request( + new Columns.All(), new Condition.Equals("id", userId))); + request.getSession().setAttribute(USER_ID_KEY, user.getId()); + LogAction.login(user.getId(), WebHelper.retrieveRemoteAddress(request)); + return user; + } + @PermitAll @POST public User add( @FormParam("email") String email, @FormParam("password") String password) throws StorageException { - User user = Context.getPermissionsManager().login(email, password); + User user = loginService.login(email, password); if (user != null) { request.getSession().setAttribute(USER_ID_KEY, user.getId()); - LogAction.login(user.getId()); + LogAction.login(user.getId(), WebHelper.retrieveRemoteAddress(request)); return user; } else { - LogAction.failedLogin(ServletHelper.retrieveRemoteAddress(request)); + LogAction.failedLogin(WebHelper.retrieveRemoteAddress(request)); throw new WebApplicationException(Response.status(Response.Status.UNAUTHORIZED).build()); } } @DELETE public Response remove() { - LogAction.logout(getUserId()); + LogAction.logout(getUserId(), WebHelper.retrieveRemoteAddress(request)); request.getSession().removeAttribute(USER_ID_KEY); return Response.noContent().build(); } + @Path("token") + @POST + public String requestToken( + @FormParam("expiration") Date expiration) throws StorageException, GeneralSecurityException, IOException { + return tokenManager.generateToken(getUserId(), expiration); + } + + @PermitAll + @Path("openid/auth") + @GET + public Response openIdAuth() throws IOException { + return Response.seeOther(openIdProvider.createAuthUri()).build(); + } + + @PermitAll + @Path("openid/callback") + @GET + public Response requestToken() throws IOException, StorageException, ParseException, GeneralSecurityException { + StringBuilder requestUrl = new StringBuilder(request.getRequestURL().toString()); + String queryString = request.getQueryString(); + String requestUri = requestUrl.append('?').append(queryString).toString(); + + return Response.seeOther(openIdProvider.handleCallback(URI.create(requestUri), request)).build(); + } } diff --git a/src/main/java/org/traccar/api/resource/StatisticsResource.java b/src/main/java/org/traccar/api/resource/StatisticsResource.java index 5c0734877..0c728c77d 100644 --- a/src/main/java/org/traccar/api/resource/StatisticsResource.java +++ b/src/main/java/org/traccar/api/resource/StatisticsResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,17 +15,20 @@ */ package org.traccar.api.resource; -import org.traccar.Context; import org.traccar.api.BaseResource; import org.traccar.model.Statistics; import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Order; +import org.traccar.storage.query.Request; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; import java.util.Collection; import java.util.Date; @@ -37,8 +40,11 @@ public class StatisticsResource extends BaseResource { @GET public Collection<Statistics> get( @QueryParam("from") Date from, @QueryParam("to") Date to) throws StorageException { - Context.getPermissionsManager().checkAdmin(getUserId()); - return Context.getDataManager().getStatistics(from, to); + permissionsService.checkAdmin(getUserId()); + return storage.getObjects(Statistics.class, new Request( + new Columns.All(), + new Condition.Between("captureTime", "from", from, "to", to), + new Order("captureTime"))); } } diff --git a/src/main/java/org/traccar/api/resource/UserResource.java b/src/main/java/org/traccar/api/resource/UserResource.java index 83bb8fd0b..cbee3bd4a 100644 --- a/src/main/java/org/traccar/api/resource/UserResource.java +++ b/src/main/java/org/traccar/api/resource/UserResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org) + * 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. @@ -15,81 +15,99 @@ */ package org.traccar.api.resource; -import org.traccar.Context; import org.traccar.api.BaseObjectResource; -import org.traccar.config.Keys; -import org.traccar.database.UsersManager; +import org.traccar.config.Config; import org.traccar.helper.LogAction; +import org.traccar.helper.model.UserUtil; import org.traccar.model.ManagedUser; +import org.traccar.model.Permission; import org.traccar.model.User; import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Request; -import javax.annotation.security.PermitAll; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import java.sql.SQLException; +import jakarta.annotation.security.PermitAll; +import jakarta.inject.Inject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import java.util.Collection; -import java.util.Date; -import java.util.Set; @Path("users") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public class UserResource extends BaseObjectResource<User> { + @Inject + private Config config; + public UserResource() { super(User.class); } @GET - public Collection<User> get(@QueryParam("userId") long userId) throws SQLException { - UsersManager usersManager = Context.getUsersManager(); - Set<Long> result; - if (Context.getPermissionsManager().getUserAdmin(getUserId())) { - if (userId != 0) { - result = usersManager.getUserItems(userId); - } else { - result = usersManager.getAllItems(); - } - } else if (Context.getPermissionsManager().getUserManager(getUserId())) { - result = usersManager.getManagedItems(getUserId()); + public Collection<User> get(@QueryParam("userId") long userId) throws StorageException { + if (userId > 0) { + permissionsService.checkUser(getUserId(), userId); + return storage.getObjects(baseClass, new Request( + new Columns.All(), + new Condition.Permission(User.class, userId, ManagedUser.class).excludeGroups())); + } else if (permissionsService.notAdmin(getUserId())) { + return storage.getObjects(baseClass, new Request( + new Columns.All(), + new Condition.Permission(User.class, getUserId(), ManagedUser.class).excludeGroups())); } else { - throw new SecurityException("Admin or manager access required"); + return storage.getObjects(baseClass, new Request(new Columns.All())); } - return usersManager.getItems(result); } @Override @PermitAll @POST public Response add(User entity) throws StorageException { - if (!Context.getPermissionsManager().getUserAdmin(getUserId())) { - Context.getPermissionsManager().checkUserUpdate(getUserId(), new User(), entity); - if (Context.getPermissionsManager().getUserManager(getUserId())) { - Context.getPermissionsManager().checkUserLimit(getUserId()); + User currentUser = getUserId() > 0 ? permissionsService.getUser(getUserId()) : null; + if (currentUser == null || !currentUser.getAdministrator()) { + permissionsService.checkUserUpdate(getUserId(), new User(), entity); + if (currentUser != null && currentUser.getUserLimit() != 0) { + int userLimit = currentUser.getUserLimit(); + if (userLimit > 0) { + int userCount = storage.getObjects(baseClass, new Request( + new Columns.All(), + new Condition.Permission(User.class, getUserId(), ManagedUser.class).excludeGroups())) + .size(); + if (userCount >= userLimit) { + throw new SecurityException("Manager user limit reached"); + } + } } else { - Context.getPermissionsManager().checkRegistration(getUserId()); - entity.setDeviceLimit(Context.getConfig().getInteger(Keys.USERS_DEFAULT_DEVICE_LIMIT)); - int expirationDays = Context.getConfig().getInteger(Keys.USERS_DEFAULT_EXPIRATION_DAYS); - if (expirationDays > 0) { - entity.setExpirationTime( - new Date(System.currentTimeMillis() + (long) expirationDays * 24 * 3600 * 1000)); + if (!permissionsService.getServer().getRegistration()) { + throw new SecurityException("Registration disabled"); } + UserUtil.setUserDefaults(entity, config); } } - Context.getUsersManager().addItem(entity); + + if (UserUtil.isEmpty(storage)) { + entity.setAdministrator(true); + } + + entity.setId(storage.addObject(entity, new Request(new Columns.Exclude("id")))); + storage.updateObject(entity, new Request( + new Columns.Include("hashedPassword", "salt"), + new Condition.Equals("id", entity.getId()))); + LogAction.create(getUserId(), entity); - if (Context.getPermissionsManager().getUserManager(getUserId())) { - Context.getDataManager().linkObject(User.class, getUserId(), ManagedUser.class, entity.getId(), true); + + if (currentUser != null && currentUser.getUserLimit() != 0) { + storage.addPermission(new Permission(User.class, getUserId(), ManagedUser.class, entity.getId())); LogAction.link(getUserId(), User.class, getUserId(), ManagedUser.class, entity.getId()); } - Context.getUsersManager().refreshUserItems(); return Response.ok(entity).build(); } diff --git a/src/main/java/org/traccar/api/security/LoginService.java b/src/main/java/org/traccar/api/security/LoginService.java new file mode 100644 index 000000000..91e964ee9 --- /dev/null +++ b/src/main/java/org/traccar/api/security/LoginService.java @@ -0,0 +1,129 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.api.security; + +import org.traccar.api.signature.TokenManager; +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.database.LdapProvider; +import org.traccar.helper.model.UserUtil; +import org.traccar.model.User; +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.annotation.Nullable; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import java.io.IOException; +import java.security.GeneralSecurityException; + +@Singleton +public class LoginService { + + private final Config config; + private final Storage storage; + private final TokenManager tokenManager; + private final LdapProvider ldapProvider; + + private final String serviceAccountToken; + private final boolean forceLdap; + private final boolean forceOpenId; + + @Inject + public LoginService( + Config config, Storage storage, TokenManager tokenManager, @Nullable LdapProvider ldapProvider) { + this.storage = storage; + this.config = config; + this.tokenManager = tokenManager; + this.ldapProvider = ldapProvider; + serviceAccountToken = config.getString(Keys.WEB_SERVICE_ACCOUNT_TOKEN); + forceLdap = config.getBoolean(Keys.LDAP_FORCE); + forceOpenId = config.getBoolean(Keys.OPENID_FORCE); + } + + public User login(String token) throws StorageException, GeneralSecurityException, IOException { + if (serviceAccountToken != null && serviceAccountToken.equals(token)) { + return new ServiceAccountUser(); + } + long userId = tokenManager.verifyToken(token); + User user = storage.getObject(User.class, new Request( + new Columns.All(), new Condition.Equals("id", userId))); + if (user != null) { + checkUserEnabled(user); + } + return user; + } + + public User login(String email, String password) throws StorageException { + if (forceOpenId) { + return null; + } + + email = email.trim(); + User user = storage.getObject(User.class, new Request( + new Columns.All(), + new Condition.Or( + new Condition.Equals("email", email), + new Condition.Equals("login", email)))); + if (user != null) { + if (ldapProvider != null && user.getLogin() != null && ldapProvider.login(user.getLogin(), password) + || !forceLdap && user.isPasswordValid(password)) { + checkUserEnabled(user); + return user; + } + } else { + if (ldapProvider != null && ldapProvider.login(email, password)) { + user = ldapProvider.getUser(email); + user.setId(storage.addObject(user, new Request(new Columns.Exclude("id")))); + checkUserEnabled(user); + return user; + } + } + return null; + } + + public User login(String email, String name, Boolean administrator) throws StorageException { + User user = storage.getObject(User.class, new Request( + new Columns.All(), + new Condition.Equals("email", email))); + + if (user != null) { + checkUserEnabled(user); + return user; + } else { + user = new User(); + UserUtil.setUserDefaults(user, config); + user.setName(name); + user.setEmail(email); + user.setFixedEmail(true); + user.setAdministrator(administrator); + user.setId(storage.addObject(user, new Request(new Columns.Exclude("id")))); + checkUserEnabled(user); + return user; + } + } + + private void checkUserEnabled(User user) throws SecurityException { + if (user == null) { + throw new SecurityException("Unknown account"); + } + user.checkDisabled(); + } + +} diff --git a/src/main/java/org/traccar/api/security/PermissionsService.java b/src/main/java/org/traccar/api/security/PermissionsService.java new file mode 100644 index 000000000..d60bbafb8 --- /dev/null +++ b/src/main/java/org/traccar/api/security/PermissionsService.java @@ -0,0 +1,225 @@ +/* + * 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.api.security; + +import com.google.inject.servlet.RequestScoped; +import org.traccar.model.BaseModel; +import org.traccar.model.Calendar; +import org.traccar.model.Command; +import org.traccar.model.Device; +import org.traccar.model.Group; +import org.traccar.model.GroupedModel; +import org.traccar.model.ManagedUser; +import org.traccar.model.Notification; +import org.traccar.model.Schedulable; +import org.traccar.model.Server; +import org.traccar.model.User; +import org.traccar.model.UserRestrictions; +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 java.util.Objects; + +@RequestScoped +public class PermissionsService { + + private final Storage storage; + + private Server server; + private User user; + + @Inject + public PermissionsService(Storage storage) { + this.storage = storage; + } + + public Server getServer() throws StorageException { + if (server == null) { + server = storage.getObject( + Server.class, new Request(new Columns.All())); + } + return server; + } + + public User getUser(long userId) throws StorageException { + if (user == null && userId > 0) { + if (userId == ServiceAccountUser.ID) { + user = new ServiceAccountUser(); + } else { + user = storage.getObject( + User.class, new Request(new Columns.All(), new Condition.Equals("id", userId))); + } + } + return user; + } + + public boolean notAdmin(long userId) throws StorageException { + return !getUser(userId).getAdministrator(); + } + + public void checkAdmin(long userId) throws StorageException, SecurityException { + if (!getUser(userId).getAdministrator()) { + throw new SecurityException("Administrator access required"); + } + } + + public void checkManager(long userId) throws StorageException, SecurityException { + if (!getUser(userId).getAdministrator() && getUser(userId).getUserLimit() == 0) { + throw new SecurityException("Manager access required"); + } + } + + public interface CheckRestrictionCallback { + boolean denied(UserRestrictions userRestrictions); + } + + public void checkRestriction( + long userId, CheckRestrictionCallback callback) throws StorageException, SecurityException { + if (!getUser(userId).getAdministrator() + && (callback.denied(getServer()) || callback.denied(getUser(userId)))) { + throw new SecurityException("Operation restricted"); + } + } + + public void checkEdit(long userId, Class<?> clazz, boolean addition) throws StorageException, SecurityException { + if (!getUser(userId).getAdministrator()) { + boolean denied = false; + if (getServer().getReadonly() || getUser(userId).getReadonly()) { + denied = true; + } else if (clazz.equals(Device.class)) { + denied = getServer().getDeviceReadonly() || getUser(userId).getDeviceReadonly() + || addition && getUser(userId).getDeviceLimit() == 0; + if (!denied && addition && getUser(userId).getDeviceLimit() > 0) { + int deviceCount = storage.getObjects(Device.class, new Request( + new Columns.Include("id"), + new Condition.Permission(User.class, userId, Device.class))).size(); + denied = deviceCount >= getUser(userId).getDeviceLimit(); + } + } else if (clazz.equals(Command.class)) { + denied = getServer().getLimitCommands() || getUser(userId).getLimitCommands(); + } + if (denied) { + throw new SecurityException("Write access denied"); + } + } + } + + public void checkEdit(long userId, BaseModel object, boolean addition) throws StorageException, SecurityException { + if (!getUser(userId).getAdministrator()) { + checkEdit(userId, object.getClass(), addition); + if (object instanceof GroupedModel) { + GroupedModel after = ((GroupedModel) object); + if (after.getGroupId() > 0) { + GroupedModel before = null; + if (!addition) { + before = storage.getObject(after.getClass(), new Request( + new Columns.Include("groupId"), new Condition.Equals("id", after.getId()))); + } + if (before == null || before.getGroupId() != after.getGroupId()) { + checkPermission(Group.class, userId, after.getGroupId()); + } + } + } + if (object instanceof Schedulable) { + Schedulable after = ((Schedulable) object); + if (after.getCalendarId() > 0) { + Schedulable before = null; + if (!addition) { + before = storage.getObject(after.getClass(), new Request( + new Columns.Include("calendarId"), new Condition.Equals("id", object.getId()))); + } + if (before == null || before.getCalendarId() != after.getCalendarId()) { + checkPermission(Calendar.class, userId, after.getCalendarId()); + } + } + } + if (object instanceof Notification) { + Notification after = ((Notification) object); + if (after.getCommandId() > 0) { + Notification before = null; + if (!addition) { + before = storage.getObject(after.getClass(), new Request( + new Columns.Include("commandId"), new Condition.Equals("id", object.getId()))); + } + if (before == null || before.getCommandId() != after.getCommandId()) { + checkPermission(Command.class, userId, after.getCommandId()); + } + } + } + } + } + + public void checkUser(long userId, long managedUserId) throws StorageException, SecurityException { + if (userId != managedUserId && !getUser(userId).getAdministrator()) { + if (!getUser(userId).getManager() + || storage.getPermissions(User.class, userId, ManagedUser.class, managedUserId).isEmpty()) { + throw new SecurityException("User access denied"); + } + } + } + + public void checkUserUpdate(long userId, User before, User after) throws StorageException, SecurityException { + if (before.getAdministrator() != after.getAdministrator() + || before.getDeviceLimit() != after.getDeviceLimit() + || before.getUserLimit() != after.getUserLimit()) { + checkAdmin(userId); + } + User user = userId > 0 ? getUser(userId) : null; + if (user != null && user.getExpirationTime() != null + && !Objects.equals(before.getExpirationTime(), after.getExpirationTime()) + && (after.getExpirationTime() == null + || user.getExpirationTime().compareTo(after.getExpirationTime()) < 0)) { + checkAdmin(userId); + } + if (before.getReadonly() != after.getReadonly() + || before.getDeviceReadonly() != after.getDeviceReadonly() + || before.getDisabled() != after.getDisabled() + || before.getLimitCommands() != after.getLimitCommands() + || before.getDisableReports() != after.getDisableReports() + || before.getFixedEmail() != after.getFixedEmail()) { + if (userId == after.getId()) { + checkAdmin(userId); + } else if (after.getId() > 0) { + checkUser(userId, after.getId()); + } else { + checkManager(userId); + } + } + if (before.getFixedEmail() && !before.getEmail().equals(after.getEmail())) { + checkAdmin(userId); + } + } + + public <T extends BaseModel> void checkPermission( + Class<T> clazz, long userId, long objectId) throws StorageException, SecurityException { + if (!getUser(userId).getAdministrator() && !(clazz.equals(User.class) && userId == objectId)) { + var object = storage.getObject(clazz, new Request( + new Columns.Include("id"), + new Condition.And( + new Condition.Equals("id", objectId), + new Condition.Permission( + User.class, userId, clazz.equals(User.class) ? ManagedUser.class : clazz)))); + if (object == null) { + throw new SecurityException(clazz.getSimpleName() + " access denied"); + } + } + } + +} diff --git a/src/main/java/org/traccar/api/security/SecurityRequestFilter.java b/src/main/java/org/traccar/api/security/SecurityRequestFilter.java index 9f20acb40..a34361854 100644 --- a/src/main/java/org/traccar/api/security/SecurityRequestFilter.java +++ b/src/main/java/org/traccar/api/security/SecurityRequestFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 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. @@ -15,37 +15,34 @@ */ package org.traccar.api.security; +import com.google.inject.Injector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.traccar.Context; -import org.traccar.Main; import org.traccar.api.resource.SessionResource; import org.traccar.database.StatisticsManager; import org.traccar.helper.DataConverter; import org.traccar.model.User; import org.traccar.storage.StorageException; -import javax.annotation.security.PermitAll; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.container.ContainerRequestContext; -import javax.ws.rs.container.ContainerRequestFilter; -import javax.ws.rs.container.ResourceInfo; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.SecurityContext; +import jakarta.annotation.security.PermitAll; +import jakarta.inject.Inject; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.container.ContainerRequestContext; +import jakarta.ws.rs.container.ContainerRequestFilter; +import jakarta.ws.rs.container.ResourceInfo; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.SecurityContext; +import java.io.IOException; import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; public class SecurityRequestFilter implements ContainerRequestFilter { private static final Logger LOGGER = LoggerFactory.getLogger(SecurityRequestFilter.class); - public static final String AUTHORIZATION_HEADER = "Authorization"; - public static final String WWW_AUTHENTICATE = "WWW-Authenticate"; - public static final String BASIC_REALM = "Basic realm=\"api\""; - public static final String X_REQUESTED_WITH = "X-Requested-With"; - public static final String XML_HTTP_REQUEST = "XMLHttpRequest"; - public static String[] decodeBasicAuth(String auth) { auth = auth.replaceFirst("[B|b]asic ", ""); byte[] decodedBytes = DataConverter.parseBase64(auth); @@ -55,12 +52,21 @@ public class SecurityRequestFilter implements ContainerRequestFilter { return null; } - @javax.ws.rs.core.Context + @Context private HttpServletRequest request; - @javax.ws.rs.core.Context + @Context private ResourceInfo resourceInfo; + @Inject + private LoginService loginService; + + @Inject + private StatisticsManager statisticsManager; + + @Inject + private Injector injector; + @Override public void filter(ContainerRequestContext requestContext) { @@ -72,17 +78,22 @@ public class SecurityRequestFilter implements ContainerRequestFilter { try { - String authHeader = requestContext.getHeaderString(AUTHORIZATION_HEADER); + String authHeader = requestContext.getHeaderString("Authorization"); if (authHeader != null) { try { - String[] auth = decodeBasicAuth(authHeader); - User user = Context.getPermissionsManager().login(auth[0], auth[1]); + User user; + if (authHeader.startsWith("Bearer ")) { + user = loginService.login(authHeader.substring(7)); + } else { + String[] auth = decodeBasicAuth(authHeader); + user = loginService.login(auth[0], auth[1]); + } if (user != null) { - Main.getInjector().getInstance(StatisticsManager.class).registerRequest(user.getId()); + statisticsManager.registerRequest(user.getId()); securityContext = new UserSecurityContext(new UserPrincipal(user.getId())); } - } catch (StorageException e) { + } catch (StorageException | GeneralSecurityException | IOException e) { throw new WebApplicationException(e); } @@ -90,14 +101,14 @@ public class SecurityRequestFilter implements ContainerRequestFilter { Long userId = (Long) request.getSession().getAttribute(SessionResource.USER_ID_KEY); if (userId != null) { - Context.getPermissionsManager().checkUserEnabled(userId); - Main.getInjector().getInstance(StatisticsManager.class).registerRequest(userId); + injector.getInstance(PermissionsService.class).getUser(userId).checkDisabled(); + statisticsManager.registerRequest(userId); securityContext = new UserSecurityContext(new UserPrincipal(userId)); } } - } catch (SecurityException e) { + } catch (SecurityException | StorageException e) { LOGGER.warn("Authentication error", e); } @@ -107,8 +118,9 @@ public class SecurityRequestFilter implements ContainerRequestFilter { Method method = resourceInfo.getResourceMethod(); if (!method.isAnnotationPresent(PermitAll.class)) { Response.ResponseBuilder responseBuilder = Response.status(Response.Status.UNAUTHORIZED); - if (!XML_HTTP_REQUEST.equals(request.getHeader(X_REQUESTED_WITH))) { - responseBuilder.header(WWW_AUTHENTICATE, BASIC_REALM); + String accept = request.getHeader("Accept"); + if (accept != null && accept.contains("text/html")) { + responseBuilder.header("WWW-Authenticate", "Basic realm=\"api\""); } throw new WebApplicationException(responseBuilder.build()); } diff --git a/src/main/java/org/traccar/database/MaintenancesManager.java b/src/main/java/org/traccar/api/security/ServiceAccountUser.java index 4e266cb78..644142434 100644 --- a/src/main/java/org/traccar/database/MaintenancesManager.java +++ b/src/main/java/org/traccar/api/security/ServiceAccountUser.java @@ -1,6 +1,5 @@ /* - * Copyright 2018 Anton Tananaev (anton@traccar.org) - * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org) + * Copyright 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,14 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.traccar.database; +package org.traccar.api.security; -import org.traccar.model.Maintenance; +import org.traccar.model.User; -public class MaintenancesManager extends ExtendedObjectManager<Maintenance> { +public class ServiceAccountUser extends User { - public MaintenancesManager(DataManager dataManager) { - super(dataManager, Maintenance.class); - } + public static final long ID = 9000000000000000000L; + public ServiceAccountUser() { + setId(ID); + setName("Service Account"); + setEmail("none"); + setAdministrator(true); + } } diff --git a/src/main/java/org/traccar/api/security/UserSecurityContext.java b/src/main/java/org/traccar/api/security/UserSecurityContext.java index 97df6b6c7..f7adeac64 100644 --- a/src/main/java/org/traccar/api/security/UserSecurityContext.java +++ b/src/main/java/org/traccar/api/security/UserSecurityContext.java @@ -15,7 +15,7 @@ */ package org.traccar.api.security; -import javax.ws.rs.core.SecurityContext; +import jakarta.ws.rs.core.SecurityContext; import java.security.Principal; public class UserSecurityContext implements SecurityContext { diff --git a/src/main/java/org/traccar/api/signature/CryptoManager.java b/src/main/java/org/traccar/api/signature/CryptoManager.java new file mode 100644 index 000000000..71f56e0fb --- /dev/null +++ b/src/main/java/org/traccar/api/signature/CryptoManager.java @@ -0,0 +1,103 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.api.signature; + +import org.traccar.storage.Storage; +import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Request; + +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Signature; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +@Singleton +public class CryptoManager { + + private final Storage storage; + + private PublicKey publicKey; + private PrivateKey privateKey; + + @Inject + public CryptoManager(Storage storage) { + this.storage = storage; + } + + public byte[] sign(byte[] data) throws GeneralSecurityException, StorageException { + if (privateKey == null) { + initializeKeys(); + } + Signature signature = Signature.getInstance("SHA256withECDSA"); + signature.initSign(privateKey); + signature.update(data); + byte[] block = signature.sign(); + byte[] combined = new byte[1 + block.length + data.length]; + combined[0] = (byte) block.length; + System.arraycopy(block, 0, combined, 1, block.length); + System.arraycopy(data, 0, combined, 1 + block.length, data.length); + return combined; + } + + public byte[] verify(byte[] data) throws GeneralSecurityException, StorageException { + if (publicKey == null) { + initializeKeys(); + } + Signature signature = Signature.getInstance("SHA256withECDSA"); + signature.initVerify(publicKey); + int length = data[0]; + byte[] originalData = new byte[data.length - 1 - length]; + System.arraycopy(data, 1 + length, originalData, 0, originalData.length); + signature.update(originalData); + if (!signature.verify(data, 1, length)) { + throw new SecurityException("Invalid signature"); + } + return originalData; + } + + private void initializeKeys() throws StorageException, GeneralSecurityException { + KeystoreModel model = storage.getObject(KeystoreModel.class, new Request(new Columns.All())); + if (model != null) { + publicKey = KeyFactory.getInstance("EC") + .generatePublic(new X509EncodedKeySpec(model.getPublicKey())); + privateKey = KeyFactory.getInstance("EC") + .generatePrivate(new PKCS8EncodedKeySpec(model.getPrivateKey())); + } else { + KeyPairGenerator generator = KeyPairGenerator.getInstance("EC"); + generator.initialize(new ECGenParameterSpec("secp256r1"), new SecureRandom()); + KeyPair pair = generator.generateKeyPair(); + + publicKey = pair.getPublic(); + privateKey = pair.getPrivate(); + + model = new KeystoreModel(); + model.setPublicKey(publicKey.getEncoded()); + model.setPrivateKey(privateKey.getEncoded()); + storage.addObject(model, new Request(new Columns.Exclude("id"))); + } + } + +} diff --git a/src/main/java/org/traccar/api/signature/KeystoreModel.java b/src/main/java/org/traccar/api/signature/KeystoreModel.java new file mode 100644 index 000000000..7f3140e81 --- /dev/null +++ b/src/main/java/org/traccar/api/signature/KeystoreModel.java @@ -0,0 +1,44 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.api.signature; + +import org.traccar.model.BaseModel; +import org.traccar.storage.StorageName; + +@StorageName("tc_keystore") +public class KeystoreModel extends BaseModel { + + private byte[] publicKey; + + public byte[] getPublicKey() { + return publicKey; + } + + public void setPublicKey(byte[] publicKey) { + this.publicKey = publicKey; + } + + private byte[] privateKey; + + public byte[] getPrivateKey() { + return privateKey; + } + + public void setPrivateKey(byte[] privateKey) { + this.privateKey = privateKey; + } + +} diff --git a/src/main/java/org/traccar/api/signature/TokenManager.java b/src/main/java/org/traccar/api/signature/TokenManager.java new file mode 100644 index 000000000..3019e12b9 --- /dev/null +++ b/src/main/java/org/traccar/api/signature/TokenManager.java @@ -0,0 +1,77 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.api.signature; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.codec.binary.Base64; +import org.traccar.storage.StorageException; + +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.util.Date; +import java.util.concurrent.TimeUnit; + +@Singleton +public class TokenManager { + + private static final int DEFAULT_EXPIRATION_DAYS = 7; + + private final ObjectMapper objectMapper; + private final CryptoManager cryptoManager; + + public static class Data { + @JsonProperty("u") + private long userId; + @JsonProperty("e") + private Date expiration; + } + + @Inject + public TokenManager(ObjectMapper objectMapper, CryptoManager cryptoManager) { + this.objectMapper = objectMapper; + this.cryptoManager = cryptoManager; + } + + public String generateToken(long userId) throws IOException, GeneralSecurityException, StorageException { + return generateToken(userId, null); + } + + public String generateToken( + long userId, Date expiration) throws IOException, GeneralSecurityException, StorageException { + Data data = new Data(); + data.userId = userId; + if (expiration != null) { + data.expiration = expiration; + } else { + data.expiration = new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(DEFAULT_EXPIRATION_DAYS)); + } + byte[] encoded = objectMapper.writeValueAsBytes(data); + return Base64.encodeBase64URLSafeString(cryptoManager.sign(encoded)); + } + + public long verifyToken(String token) throws IOException, GeneralSecurityException, StorageException { + byte[] encoded = cryptoManager.verify(Base64.decodeBase64(token)); + Data data = objectMapper.readValue(encoded, Data.class); + if (data.expiration.before(new Date())) { + throw new SecurityException("Token has expired"); + } + return data.userId; + } + +} diff --git a/src/main/java/org/traccar/broadcast/BaseBroadcastService.java b/src/main/java/org/traccar/broadcast/BaseBroadcastService.java new file mode 100644 index 000000000..a95d333f2 --- /dev/null +++ b/src/main/java/org/traccar/broadcast/BaseBroadcastService.java @@ -0,0 +1,118 @@ +/* + * 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.broadcast; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.traccar.model.BaseModel; +import org.traccar.model.Device; +import org.traccar.model.Event; +import org.traccar.model.Permission; +import org.traccar.model.Position; + +public abstract class BaseBroadcastService implements BroadcastService { + + private final Set<BroadcastInterface> listeners = new HashSet<>(); + + @Override + public boolean singleInstance() { + return true; + } + + @Override + public void registerListener(BroadcastInterface listener) { + listeners.add(listener); + } + + @Override + public void updateDevice(boolean local, Device device) { + BroadcastMessage message = new BroadcastMessage(); + message.setDevice(device); + sendMessage(message); + } + + @Override + public void updatePosition(boolean local, Position position) { + BroadcastMessage message = new BroadcastMessage(); + message.setPosition(position); + sendMessage(message); + } + + @Override + public void updateEvent(boolean local, long userId, Event event) { + BroadcastMessage message = new BroadcastMessage(); + message.setUserId(userId); + message.setEvent(event); + sendMessage(message); + } + + @Override + public void updateCommand(boolean local, long deviceId) { + BroadcastMessage message = new BroadcastMessage(); + message.setCommandDeviceId(deviceId); + sendMessage(message); + } + + @Override + public void invalidateObject(boolean local, Class<? extends BaseModel> clazz, long id) { + BroadcastMessage message = new BroadcastMessage(); + message.setChanges(Map.of(Permission.getKey(clazz), id)); + sendMessage(message); + } + + @Override + public void invalidatePermission( + boolean local, + Class<? extends BaseModel> clazz1, long id1, + Class<? extends BaseModel> clazz2, long id2) { + BroadcastMessage message = new BroadcastMessage(); + message.setChanges(Map.of(Permission.getKey(clazz1), id1, Permission.getKey(clazz2), id2)); + sendMessage(message); + } + + protected abstract void sendMessage(BroadcastMessage message); + + protected void handleMessage(BroadcastMessage message) { + if (message.getDevice() != null) { + listeners.forEach(listener -> listener.updateDevice(false, message.getDevice())); + } else if (message.getPosition() != null) { + listeners.forEach(listener -> listener.updatePosition(false, message.getPosition())); + } else if (message.getUserId() != null && message.getEvent() != null) { + listeners.forEach(listener -> listener.updateEvent(false, message.getUserId(), message.getEvent())); + } else if (message.getCommandDeviceId() != null) { + listeners.forEach(listener -> listener.updateCommand(false, message.getCommandDeviceId())); + } else if (message.getChanges() != null) { + var iterator = message.getChanges().entrySet().iterator(); + if (iterator.hasNext()) { + var first = iterator.next(); + if (iterator.hasNext()) { + var second = iterator.next(); + listeners.forEach(listener -> listener.invalidatePermission( + false, + Permission.getKeyClass(first.getKey()), first.getValue(), + Permission.getKeyClass(second.getKey()), second.getValue())); + } else { + listeners.forEach(listener -> listener.invalidateObject( + false, + Permission.getKeyClass(first.getKey()), first.getValue())); + } + } + } + } + +} diff --git a/src/main/java/org/traccar/broadcast/BroadcastInterface.java b/src/main/java/org/traccar/broadcast/BroadcastInterface.java new file mode 100644 index 000000000..673ebd8b8 --- /dev/null +++ b/src/main/java/org/traccar/broadcast/BroadcastInterface.java @@ -0,0 +1,45 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.broadcast; + +import org.traccar.model.BaseModel; +import org.traccar.model.Device; +import org.traccar.model.Event; +import org.traccar.model.Position; + +public interface BroadcastInterface { + + default void updateDevice(boolean local, Device device) { + } + + default void updatePosition(boolean local, Position position) { + } + + default void updateEvent(boolean local, long userId, Event event) { + } + + default void updateCommand(boolean local, long deviceId) { + } + + default void invalidateObject(boolean local, Class<? extends BaseModel> clazz, long id) { + } + + default void invalidatePermission( + boolean local, + Class<? extends BaseModel> clazz1, long id1, + Class<? extends BaseModel> clazz2, long id2) { + } +} diff --git a/src/main/java/org/traccar/broadcast/BroadcastMessage.java b/src/main/java/org/traccar/broadcast/BroadcastMessage.java new file mode 100644 index 000000000..985848d04 --- /dev/null +++ b/src/main/java/org/traccar/broadcast/BroadcastMessage.java @@ -0,0 +1,85 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.broadcast; + +import org.traccar.model.Device; +import org.traccar.model.Event; +import org.traccar.model.Position; + +import java.util.Map; + +public class BroadcastMessage { + + private Device device; + + public Device getDevice() { + return device; + } + + public void setDevice(Device device) { + this.device = device; + } + + private Position position; + + public Position getPosition() { + return position; + } + + public void setPosition(Position position) { + this.position = position; + } + + private Long userId; + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + private Event event; + + public Event getEvent() { + return event; + } + + public void setEvent(Event event) { + this.event = event; + } + + private Long commandDeviceId; + + public Long getCommandDeviceId() { + return commandDeviceId; + } + + public void setCommandDeviceId(Long commandDeviceId) { + this.commandDeviceId = commandDeviceId; + } + + private Map<String, Long> changes; + + public Map<String, Long> getChanges() { + return changes; + } + + public void setChanges(Map<String, Long> changes) { + this.changes = changes; + } +} diff --git a/src/main/java/org/traccar/broadcast/BroadcastService.java b/src/main/java/org/traccar/broadcast/BroadcastService.java new file mode 100644 index 000000000..a86c43b5b --- /dev/null +++ b/src/main/java/org/traccar/broadcast/BroadcastService.java @@ -0,0 +1,23 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.broadcast; + +import org.traccar.LifecycleObject; + +public interface BroadcastService extends LifecycleObject, BroadcastInterface { + boolean singleInstance(); + void registerListener(BroadcastInterface listener); +} diff --git a/src/main/java/org/traccar/broadcast/MulticastBroadcastService.java b/src/main/java/org/traccar/broadcast/MulticastBroadcastService.java new file mode 100644 index 000000000..1c02b319b --- /dev/null +++ b/src/main/java/org/traccar/broadcast/MulticastBroadcastService.java @@ -0,0 +1,112 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.broadcast; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.config.Config; +import org.traccar.config.Keys; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.MulticastSocket; +import java.net.NetworkInterface; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class MulticastBroadcastService extends BaseBroadcastService { + + private static final Logger LOGGER = LoggerFactory.getLogger(MulticastBroadcastService.class); + + private final ObjectMapper objectMapper; + + private final NetworkInterface networkInterface; + private final int port; + private final InetSocketAddress group; + + private DatagramSocket publisherSocket; + + private final ExecutorService service = Executors.newSingleThreadExecutor(); + private final byte[] receiverBuffer = new byte[4096]; + + public MulticastBroadcastService(Config config, ObjectMapper objectMapper) throws IOException { + this.objectMapper = objectMapper; + port = config.getInteger(Keys.BROADCAST_PORT); + String interfaceName = config.getString(Keys.BROADCAST_INTERFACE); + if (interfaceName.indexOf('.') >= 0 || interfaceName.indexOf(':') >= 0) { + networkInterface = NetworkInterface.getByInetAddress(InetAddress.getByName(interfaceName)); + } else { + networkInterface = NetworkInterface.getByName(interfaceName); + } + InetAddress address = InetAddress.getByName(config.getString(Keys.BROADCAST_ADDRESS)); + group = new InetSocketAddress(address, port); + } + + @Override + public boolean singleInstance() { + return false; + } + + @Override + protected void sendMessage(BroadcastMessage message) { + try { + byte[] buffer = objectMapper.writeValueAsString(message).getBytes(StandardCharsets.UTF_8); + DatagramPacket packet = new DatagramPacket(buffer, buffer.length, group); + publisherSocket.send(packet); + } catch (IOException e) { + LOGGER.warn("Broadcast failed", e); + } + } + + @Override + public void start() throws IOException { + service.submit(receiver); + } + + @Override + public void stop() { + service.shutdown(); + } + + private final Runnable receiver = new Runnable() { + @Override + public void run() { + try (MulticastSocket socket = new MulticastSocket(port)) { + socket.setNetworkInterface(networkInterface); + socket.joinGroup(group, networkInterface); + publisherSocket = socket; + while (!service.isShutdown()) { + DatagramPacket packet = new DatagramPacket(receiverBuffer, receiverBuffer.length); + socket.receive(packet); + if (networkInterface.inetAddresses().noneMatch(a -> a.equals(packet.getAddress()))) { + String data = new String(packet.getData(), 0, packet.getLength(), StandardCharsets.UTF_8); + handleMessage(objectMapper.readValue(data, BroadcastMessage.class)); + } + } + publisherSocket = null; + socket.leaveGroup(group, networkInterface); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + +} diff --git a/src/main/java/org/traccar/api/ObjectMapperProvider.java b/src/main/java/org/traccar/broadcast/NullBroadcastService.java index f81b20917..f95037990 100644 --- a/src/main/java/org/traccar/api/ObjectMapperProvider.java +++ b/src/main/java/org/traccar/broadcast/NullBroadcastService.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,20 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.traccar.api; +package org.traccar.broadcast; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.traccar.Context; +public class NullBroadcastService implements BroadcastService { -import javax.ws.rs.ext.ContextResolver; -import javax.ws.rs.ext.Provider; + @Override + public boolean singleInstance() { + return true; + } -@Provider -public class ObjectMapperProvider implements ContextResolver<ObjectMapper> { + @Override + public void registerListener(BroadcastInterface listener) { + } @Override - public ObjectMapper getContext(Class<?> type) { - return Context.getObjectMapper(); + public void start() throws Exception { } + @Override + public void stop() throws Exception { + } } diff --git a/src/main/java/org/traccar/broadcast/RedisBroadcastService.java b/src/main/java/org/traccar/broadcast/RedisBroadcastService.java new file mode 100644 index 000000000..e87ad5e61 --- /dev/null +++ b/src/main/java/org/traccar/broadcast/RedisBroadcastService.java @@ -0,0 +1,130 @@ +/* + * 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.broadcast; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.config.Config; +import org.traccar.config.Keys; + +import java.io.IOException; +import java.util.UUID; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPubSub; +import redis.clients.jedis.exceptions.JedisConnectionException; +import redis.clients.jedis.exceptions.JedisException; + +public class RedisBroadcastService extends BaseBroadcastService { + + private static final Logger LOGGER = LoggerFactory.getLogger(RedisBroadcastService.class); + + private final ObjectMapper objectMapper; + + private final ExecutorService service = Executors.newSingleThreadExecutor(); + + private final String url; + private final String channel = "traccar"; + + private Jedis subscriber; + private Jedis publisher; + + private final String id = UUID.randomUUID().toString(); + + public RedisBroadcastService(Config config, ObjectMapper objectMapper) throws IOException { + this.objectMapper = objectMapper; + url = config.getString(Keys.BROADCAST_ADDRESS); + + try { + subscriber = new Jedis(url); + publisher = new Jedis(url); + subscriber.connect(); + } catch (JedisConnectionException e) { + throw new IOException(e); + } + } + + @Override + public boolean singleInstance() { + return false; + } + + @Override + protected void sendMessage(BroadcastMessage message) { + try { + String payload = id + ":" + objectMapper.writeValueAsString(message); + publisher.publish(channel, payload); + } catch (IOException e) { + LOGGER.warn("Broadcast failed", e); + } catch (JedisConnectionException e) { + LOGGER.warn("Broadcast failed", e); + } + } + + @Override + public void start() throws IOException { + service.submit(receiver); + } + + @Override + public void stop() { + try { + if (subscriber != null) { + subscriber.close(); + subscriber = null; + } + } catch (JedisException e) { + LOGGER.warn("Subscriber close failed", e); + } + try { + if (publisher != null) { + publisher.close(); + publisher = null; + } + } catch (JedisException e) { + LOGGER.warn("Publisher close failed", e); + } + service.shutdown(); + } + + private final Runnable receiver = new Runnable() { + @Override + public void run() { + try { + subscriber.subscribe(new JedisPubSub() { + @Override + public void onMessage(String messageChannel, String message) { + try { + String[] parts = message.split(":", 2); + if (messageChannel.equals(channel) && parts.length == 2 && !id.equals(parts[0])) { + handleMessage(objectMapper.readValue(parts[1], BroadcastMessage.class)); + } + } catch (IOException e) { + LOGGER.warn("Broadcast handleMessage failed", e); + } + } + }, channel); + } catch (JedisConnectionException e) { + throw new RuntimeException(e); + } catch (JedisException e) { + throw new RuntimeException(e); + } + } + }; + +} diff --git a/src/main/java/org/traccar/config/Config.java b/src/main/java/org/traccar/config/Config.java index 815a6e86a..47e1f0707 100644 --- a/src/main/java/org/traccar/config/Config.java +++ b/src/main/java/org/traccar/config/Config.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org) + * 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. @@ -16,13 +16,19 @@ package org.traccar.config; import com.google.common.annotations.VisibleForTesting; +import com.google.inject.name.Named; +import org.traccar.helper.Log; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.InvalidPropertiesFormatException; +import java.util.Objects; import java.util.Properties; +@Singleton public class Config { private final Properties properties = new Properties(); @@ -32,7 +38,8 @@ public class Config { public Config() { } - public Config(String file) throws IOException { + @Inject + public Config(@Named("configFile") String file) throws IOException { try { Properties mainProperties = new Properties(); try (InputStream inputStream = new FileInputStream(file)) { @@ -50,8 +57,14 @@ public class Config { useEnvironmentVariables = Boolean.parseBoolean(System.getenv("CONFIG_USE_ENVIRONMENT_VARIABLES")) || Boolean.parseBoolean(properties.getProperty("config.useEnvironmentVariables")); + + Log.setupLogger(this); } catch (InvalidPropertiesFormatException e) { + Log.setupDefaultLogger(); throw new RuntimeException("Configuration file is not a valid XML document", e); + } catch (Exception e) { + Log.setupDefaultLogger(); + throw e; } } @@ -59,8 +72,7 @@ public class Config { return hasKey(key.getKey()); } - @Deprecated - public boolean hasKey(String key) { + private boolean hasKey(String key) { return useEnvironmentVariables && System.getenv().containsKey(getEnvironmentVariableName(key)) || properties.containsKey(key); } @@ -90,12 +102,7 @@ public class Config { } public boolean getBoolean(ConfigKey<Boolean> key) { - return getBoolean(key.getKey()); - } - - @Deprecated - public boolean getBoolean(String key) { - return Boolean.parseBoolean(getString(key)); + return Boolean.parseBoolean(getString(key.getKey())); } public int getInteger(ConfigKey<Integer> key) { @@ -104,11 +111,7 @@ public class Config { return Integer.parseInt(value); } else { Integer defaultValue = key.getDefaultValue(); - if (defaultValue != null) { - return defaultValue; - } else { - return 0; - } + return Objects.requireNonNullElse(defaultValue, 0); } } @@ -127,11 +130,7 @@ public class Config { return Long.parseLong(value); } else { Long defaultValue = key.getDefaultValue(); - if (defaultValue != null) { - return defaultValue; - } else { - return 0; - } + return Objects.requireNonNullElse(defaultValue, 0L); } } @@ -141,11 +140,7 @@ public class Config { return Double.parseDouble(value); } else { Double defaultValue = key.getDefaultValue(); - if (defaultValue != null) { - return defaultValue; - } else { - return 0; - } + return Objects.requireNonNullElse(defaultValue, 0.0); } } diff --git a/src/main/java/org/traccar/config/ConfigKey.java b/src/main/java/org/traccar/config/ConfigKey.java index c046a46a5..b8151392c 100644 --- a/src/main/java/org/traccar/config/ConfigKey.java +++ b/src/main/java/org/traccar/config/ConfigKey.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2019 - 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. @@ -15,30 +15,34 @@ */ package org.traccar.config; +import java.util.HashSet; import java.util.List; +import java.util.Set; -public class ConfigKey<T> { +public abstract class ConfigKey<T> { private final String key; - private final List<KeyType> types; + private final Set<KeyType> types = new HashSet<>(); + private final Class<T> valueClass; private final T defaultValue; - ConfigKey(String key, List<KeyType> types) { - this(key, types, null); - } - - ConfigKey(String key, List<KeyType> types, T defaultValue) { + ConfigKey(String key, List<KeyType> types, Class<T> valueClass, T defaultValue) { this.key = key; - this.types = types; + this.types.addAll(types); + this.valueClass = valueClass; this.defaultValue = defaultValue; } - String getKey() { + public String getKey() { return key; } - public List<KeyType> getTypes() { - return types; + public boolean hasType(KeyType type) { + return types.contains(type); + } + + public Class<T> getValueClass() { + return valueClass; } public T getDefaultValue() { @@ -46,3 +50,48 @@ public class ConfigKey<T> { } } + +class StringConfigKey extends ConfigKey<String> { + StringConfigKey(String key, List<KeyType> types) { + super(key, types, String.class, null); + } + StringConfigKey(String key, List<KeyType> types, String defaultValue) { + super(key, types, String.class, defaultValue); + } +} + +class BooleanConfigKey extends ConfigKey<Boolean> { + BooleanConfigKey(String key, List<KeyType> types) { + super(key, types, Boolean.class, null); + } + BooleanConfigKey(String key, List<KeyType> types, Boolean defaultValue) { + super(key, types, Boolean.class, defaultValue); + } +} + +class IntegerConfigKey extends ConfigKey<Integer> { + IntegerConfigKey(String key, List<KeyType> types) { + super(key, types, Integer.class, null); + } + IntegerConfigKey(String key, List<KeyType> types, Integer defaultValue) { + super(key, types, Integer.class, defaultValue); + } +} + +class LongConfigKey extends ConfigKey<Long> { + LongConfigKey(String key, List<KeyType> types) { + super(key, types, Long.class, null); + } + LongConfigKey(String key, List<KeyType> types, Long defaultValue) { + super(key, types, Long.class, defaultValue); + } +} + +class DoubleConfigKey extends ConfigKey<Double> { + DoubleConfigKey(String key, List<KeyType> types) { + super(key, types, Double.class, null); + } + DoubleConfigKey(String key, List<KeyType> types, Double defaultValue) { + super(key, types, Double.class, defaultValue); + } +} diff --git a/src/main/java/org/traccar/config/ConfigSuffix.java b/src/main/java/org/traccar/config/ConfigSuffix.java index ede4c107d..aac3219c6 100644 --- a/src/main/java/org/traccar/config/ConfigSuffix.java +++ b/src/main/java/org/traccar/config/ConfigSuffix.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2019 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,15 +17,11 @@ package org.traccar.config; import java.util.List; -public class ConfigSuffix<T> { +public abstract class ConfigSuffix<T> { - private final String keySuffix; - private final List<KeyType> types; - private final T defaultValue; - - ConfigSuffix(String keySuffix, List<KeyType> types) { - this(keySuffix, types, null); - } + protected final String keySuffix; + protected final List<KeyType> types; + protected final T defaultValue; ConfigSuffix(String keySuffix, List<KeyType> types, T defaultValue) { this.keySuffix = keySuffix; @@ -33,8 +29,71 @@ public class ConfigSuffix<T> { this.defaultValue = defaultValue; } - public ConfigKey<T> withPrefix(String prefix) { - return new ConfigKey<>(prefix + keySuffix, types, defaultValue); + public abstract ConfigKey<T> withPrefix(String prefix); + +} + +class StringConfigSuffix extends ConfigSuffix<String> { + StringConfigSuffix(String key, List<KeyType> types) { + super(key, types, null); + } + StringConfigSuffix(String key, List<KeyType> types, String defaultValue) { + super(key, types, defaultValue); + } + @Override + public ConfigKey<String> withPrefix(String prefix) { + return new StringConfigKey(prefix + keySuffix, types, defaultValue); + } +} + +class BooleanConfigSuffix extends ConfigSuffix<Boolean> { + BooleanConfigSuffix(String key, List<KeyType> types) { + super(key, types, null); + } + BooleanConfigSuffix(String key, List<KeyType> types, Boolean defaultValue) { + super(key, types, defaultValue); + } + @Override + public ConfigKey<Boolean> withPrefix(String prefix) { + return new BooleanConfigKey(prefix + keySuffix, types, defaultValue); + } +} + +class IntegerConfigSuffix extends ConfigSuffix<Integer> { + IntegerConfigSuffix(String key, List<KeyType> types) { + super(key, types, null); + } + IntegerConfigSuffix(String key, List<KeyType> types, Integer defaultValue) { + super(key, types, defaultValue); + } + @Override + public ConfigKey<Integer> withPrefix(String prefix) { + return new IntegerConfigKey(prefix + keySuffix, types, defaultValue); } +} +class LongConfigSuffix extends ConfigSuffix<Long> { + LongConfigSuffix(String key, List<KeyType> types) { + super(key, types, null); + } + LongConfigSuffix(String key, List<KeyType> types, Long defaultValue) { + super(key, types, defaultValue); + } + @Override + public ConfigKey<Long> withPrefix(String prefix) { + return new LongConfigKey(prefix + keySuffix, types, defaultValue); + } +} + +class DoubleConfigSuffix extends ConfigSuffix<Double> { + DoubleConfigSuffix(String key, List<KeyType> types) { + super(key, types, null); + } + DoubleConfigSuffix(String key, List<KeyType> types, Double defaultValue) { + super(key, types, defaultValue); + } + @Override + public ConfigKey<Double> withPrefix(String prefix) { + return new DoubleConfigKey(prefix + keySuffix, types, defaultValue); + } } diff --git a/src/main/java/org/traccar/config/KeyType.java b/src/main/java/org/traccar/config/KeyType.java index 57a95c9ec..46628f9fc 100644 --- a/src/main/java/org/traccar/config/KeyType.java +++ b/src/main/java/org/traccar/config/KeyType.java @@ -16,7 +16,7 @@ package org.traccar.config; public enum KeyType { - GLOBAL, + CONFIG, SERVER, USER, DEVICE, diff --git a/src/main/java/org/traccar/config/Keys.java b/src/main/java/org/traccar/config/Keys.java index ccfe4bee7..27f5f0921 100644 --- a/src/main/java/org/traccar/config/Keys.java +++ b/src/main/java/org/traccar/config/Keys.java @@ -15,7 +15,7 @@ */ package org.traccar.config; -import java.util.Collections; +import java.util.List; public final class Keys { @@ -25,39 +25,39 @@ public final class Keys { /** * Network interface for a the protocol. If not specified, server will bind all interfaces. */ - public static final ConfigSuffix<String> PROTOCOL_ADDRESS = new ConfigSuffix<>( + public static final ConfigSuffix<String> PROTOCOL_ADDRESS = new StringConfigSuffix( ".address", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Port number for the protocol. Most protocols use TCP on the transport layer. Some protocols use UDP. Some * support both TCP and UDP. */ - public static final ConfigSuffix<Integer> PROTOCOL_PORT = new ConfigSuffix<>( + public static final ConfigSuffix<Integer> PROTOCOL_PORT = new IntegerConfigSuffix( ".port", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * List of devices for polling protocols. List should contain unique ids separated by commas. Used only for polling * protocols. */ - public static final ConfigSuffix<String> PROTOCOL_DEVICES = new ConfigSuffix<>( + public static final ConfigSuffix<String> PROTOCOL_DEVICES = new StringConfigSuffix( ".devices", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Polling interval in seconds. Used only for polling protocols. */ - public static final ConfigSuffix<Long> PROTOCOL_INTERVAL = new ConfigSuffix<>( + public static final ConfigSuffix<Long> PROTOCOL_INTERVAL = new LongConfigSuffix( ".interval", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Enable SSL support for the protocol. Not all protocols support this. */ - public static final ConfigSuffix<Boolean> PROTOCOL_SSL = new ConfigSuffix<>( + public static final ConfigSuffix<Boolean> PROTOCOL_SSL = new BooleanConfigSuffix( ".ssl", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Connection timeout value in seconds. Because sometimes there is no way to detect lost TCP connection old @@ -65,574 +65,823 @@ public final class Keys { * problems with establishing new connections when number of devices is high or devices data connections are * unstable. */ - public static final ConfigSuffix<Integer> PROTOCOL_TIMEOUT = new ConfigSuffix<>( + public static final ConfigSuffix<Integer> PROTOCOL_TIMEOUT = new IntegerConfigSuffix( ".timeout", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Device password. Commonly used in some protocol for sending commands. */ - public static final ConfigSuffix<String> PROTOCOL_DEVICE_PASSWORD = new ConfigSuffix<>( + public static final ConfigKey<String> DEVICE_PASSWORD = new StringConfigKey( + "devicePassword", + List.of(KeyType.DEVICE)); + + /** + * Device password. Commonly used in some protocol for sending commands. + */ + public static final ConfigSuffix<String> PROTOCOL_DEVICE_PASSWORD = new StringConfigSuffix( ".devicePassword", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Default protocol mask to use. Currently used only by Skypatrol protocol. */ - public static final ConfigSuffix<Integer> PROTOCOL_MASK = new ConfigSuffix<>( + public static final ConfigSuffix<Integer> PROTOCOL_MASK = new IntegerConfigSuffix( ".mask", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Custom message length. Currently used only by H2 protocol for specifying binary message length. */ - public static final ConfigSuffix<Integer> PROTOCOL_MESSAGE_LENGTH = new ConfigSuffix<>( + public static final ConfigSuffix<Integer> PROTOCOL_MESSAGE_LENGTH = new IntegerConfigSuffix( ".messageLength", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Enable extended functionality for the protocol. The reason it's disabled by default is that not all devices * support it. */ - public static final ConfigSuffix<Boolean> PROTOCOL_EXTENDED = new ConfigSuffix<>( + public static final ConfigSuffix<Boolean> PROTOCOL_EXTENDED = new BooleanConfigSuffix( ".extended", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Decode string as UTF8 instead of ASCII. Only applicable for some protocols. */ - public static final ConfigSuffix<Boolean> PROTOCOL_UTF8 = new ConfigSuffix<>( + public static final ConfigSuffix<Boolean> PROTOCOL_UTF8 = new BooleanConfigSuffix( ".utf8", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Enable CAN decoding for the protocol. Similar to 'extended' configuration, it's not supported for some devices. */ - public static final ConfigSuffix<Boolean> PROTOCOL_CAN = new ConfigSuffix<>( + public static final ConfigSuffix<Boolean> PROTOCOL_CAN = new BooleanConfigSuffix( ".can", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Indicates whether server acknowledgement is required. Only applicable for some protocols. */ - public static final ConfigSuffix<Boolean> PROTOCOL_ACK = new ConfigSuffix<>( + public static final ConfigSuffix<Boolean> PROTOCOL_ACK = new BooleanConfigSuffix( ".ack", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG, KeyType.DEVICE), + false); /** * Ignore device reported fix time. Useful in case some devices report invalid time. Currently only available for * GL200 protocol. */ - public static final ConfigSuffix<Boolean> PROTOCOL_IGNORE_FIX_TIME = new ConfigSuffix<>( + public static final ConfigSuffix<Boolean> PROTOCOL_IGNORE_FIX_TIME = new BooleanConfigSuffix( ".ignoreFixTime", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Decode additional TK103 attributes. Not supported for some devices. */ - public static final ConfigSuffix<Boolean> PROTOCOL_DECODE_LOW = new ConfigSuffix<>( + public static final ConfigSuffix<Boolean> PROTOCOL_DECODE_LOW = new BooleanConfigSuffix( ".decodeLow", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Use long date format for Atrack protocol. */ - public static final ConfigSuffix<Boolean> PROTOCOL_LONG_DATE = new ConfigSuffix<>( + public static final ConfigSuffix<Boolean> PROTOCOL_LONG_DATE = new BooleanConfigSuffix( ".longDate", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Use decimal fuel value format for Atrack protocol. */ - public static final ConfigSuffix<Boolean> PROTOCOL_DECIMAL_FUEL = new ConfigSuffix<>( + public static final ConfigSuffix<Boolean> PROTOCOL_DECIMAL_FUEL = new BooleanConfigSuffix( ".decimalFuel", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Indicates additional custom attributes for Atrack protocol. */ - public static final ConfigSuffix<Boolean> PROTOCOL_CUSTOM = new ConfigSuffix<>( + public static final ConfigSuffix<Boolean> PROTOCOL_CUSTOM = new BooleanConfigSuffix( ".custom", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Custom format string for Atrack protocol. */ - public static final ConfigSuffix<String> PROTOCOL_FORM = new ConfigSuffix<>( + public static final ConfigSuffix<String> PROTOCOL_FORM = new StringConfigSuffix( ".form", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Protocol configuration. Required for some devices for decoding incoming data. */ - public static final ConfigSuffix<String> PROTOCOL_CONFIG = new ConfigSuffix<>( + public static final ConfigSuffix<String> PROTOCOL_CONFIG = new StringConfigSuffix( ".config", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Alarm mapping for Atrack protocol. */ - public static final ConfigSuffix<String> PROTOCOL_ALARM_MAP = new ConfigSuffix<>( + public static final ConfigSuffix<String> PROTOCOL_ALARM_MAP = new StringConfigSuffix( ".alarmMap", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Indicates whether TAIP protocol should have prefixes for messages. */ - public static final ConfigSuffix<Boolean> PROTOCOL_PREFIX = new ConfigSuffix<>( + public static final ConfigSuffix<Boolean> PROTOCOL_PREFIX = new BooleanConfigSuffix( ".prefix", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Some devices require server address confirmation. Use this parameter to configure correct public address. */ - public static final ConfigSuffix<String> PROTOCOL_SERVER = new ConfigSuffix<>( + public static final ConfigSuffix<String> PROTOCOL_SERVER = new StringConfigSuffix( ".server", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** - * Skip device connection session cache. Per protocol configuration. + * Protocol type for Suntech. */ - public static final ConfigSuffix<Boolean> PROTOCOL_IGNORE_SESSIONS_CACHE = new ConfigSuffix<>( - ".ignoreSessionCache", - Collections.singletonList(KeyType.GLOBAL)); + public static final ConfigKey<Integer> PROTOCOL_TYPE = new IntegerConfigKey( + "suntech.protocolType", + List.of(KeyType.CONFIG, KeyType.DEVICE)); + + /** + * Suntech HBM configuration value. + */ + public static final ConfigKey<Boolean> PROTOCOL_HBM = new BooleanConfigKey( + "suntech.hbm", + List.of(KeyType.CONFIG, KeyType.DEVICE)); + + /** + * Format includes ADC value. + */ + public static final ConfigSuffix<Boolean> PROTOCOL_INCLUDE_ADC = new BooleanConfigSuffix( + ".includeAdc", + List.of(KeyType.CONFIG, KeyType.DEVICE)); + + /** + * Format includes RPM value. + */ + public static final ConfigSuffix<Boolean> PROTOCOL_INCLUDE_RPM = new BooleanConfigSuffix( + ".includeRpm", + List.of(KeyType.CONFIG, KeyType.DEVICE)); + + /** + * Format includes temperature values. + */ + public static final ConfigSuffix<Boolean> PROTOCOL_INCLUDE_TEMPERATURE = new BooleanConfigSuffix( + ".includeTemp", + List.of(KeyType.CONFIG, KeyType.DEVICE)); + + /** + * Disable commands for the protocol. Not all protocols support this option. + */ + public static final ConfigSuffix<Boolean> PROTOCOL_DISABLE_COMMANDS = new BooleanConfigSuffix( + ".disableCommands", + List.of(KeyType.CONFIG)); + + /** + * Protocol format. Used by protocols that have configurable message format. + */ + public static final ConfigSuffix<String> PROTOCOL_FORMAT = new StringConfigSuffix( + ".format", + List.of(KeyType.CONFIG, KeyType.DEVICE)); + + /** + * Protocol date format. Used by protocols that have configurable date format. + */ + public static final ConfigSuffix<String> PROTOCOL_DATE_FORMAT = new StringConfigSuffix( + ".dateFormat", + List.of(KeyType.DEVICE)); + + /** + * Device time zone. Most devices report UTC time, but in some cases devices report local time, so this parameter + * needs to be configured for the server to be able to decode the time correctly. + */ + public static final ConfigKey<String> DECODER_TIMEZONE = new StringConfigKey( + "decoder.timezone", + List.of(KeyType.CONFIG, KeyType.DEVICE)); /** * ORBCOMM API access id. */ - public static final ConfigKey<String> ORBCOMM_ACCESS_ID = new ConfigKey<>( + public static final ConfigKey<String> ORBCOMM_ACCESS_ID = new StringConfigKey( "orbcomm.accessId", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * ORBCOMM API password. */ - public static final ConfigKey<String> ORBCOMM_PASSWORD = new ConfigKey<>( + public static final ConfigKey<String> ORBCOMM_PASSWORD = new StringConfigKey( "orbcomm.password", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** - * Skip device connection session cache. Global configuration. + * Use alternative format for the protocol of commands. */ - public static final ConfigKey<Boolean> DECODER_IGNORE_SESSIONS_CACHE = new ConfigKey<>( - "decoder.ignoreSessionCache", - Collections.singletonList(KeyType.GLOBAL)); + public static final ConfigSuffix<Boolean> PROTOCOL_ALTERNATIVE = new BooleanConfigSuffix( + ".alternative", + List.of(KeyType.CONFIG, KeyType.DEVICE), + false); + + /** + * Protocol format includes a language field. + */ + public static final ConfigSuffix<Boolean> PROTOCOL_LANGUAGE = new BooleanConfigSuffix( + ".language", + List.of(KeyType.CONFIG, KeyType.DEVICE), + false); /** * Server wide connection timeout value in seconds. See protocol timeout for more information. */ - public static final ConfigKey<Integer> SERVER_TIMEOUT = new ConfigKey<>( + public static final ConfigKey<Integer> SERVER_TIMEOUT = new IntegerConfigKey( "server.timeout", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); + + /** + * Send device responses immediately before writing it in the database. + */ + public static final ConfigKey<Boolean> SERVER_INSTANT_ACKNOWLEDGEMENT = new BooleanConfigKey( + "server.instantAcknowledgement", + List.of(KeyType.CONFIG)); /** * Address for uploading aggregated anonymous usage statistics. Uploaded information is the same you can see on the * statistics screen in the web app. It does not include any sensitive (e.g. locations). */ - public static final ConfigKey<String> SERVER_STATISTICS = new ConfigKey<>( + public static final ConfigKey<String> SERVER_STATISTICS = new StringConfigKey( "server.statistics", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); + + /** + * Fuel drop threshold value. When fuel level drops from one position to another for more the value, an event is + * generated. + */ + public static final ConfigKey<Double> EVENT_FUEL_DROP_THRESHOLD = new DoubleConfigKey( + "fuelDropThreshold", + List.of(KeyType.SERVER, KeyType.DEVICE), + 0.0); + + /** + * Fuel increase threshold value. When fuel level increases from one position to another for more the value, an + * event is generated. + */ + public static final ConfigKey<Double> EVENT_FUEL_INCREASE_THRESHOLD = new DoubleConfigKey( + "fuelIncreaseThreshold", + List.of(KeyType.SERVER, KeyType.DEVICE), + 0.0); + + /** + * Speed limit value in knots. + */ + public static final ConfigKey<Double> EVENT_OVERSPEED_LIMIT = new DoubleConfigKey( + "speedLimit", + List.of(KeyType.SERVER, KeyType.DEVICE), + 0.0); /** - * If true, the event is generated once at the beginning of overspeeding period. + * Speed limit threshold multiplier. For example, if the speed limit is 100, but we only want to generate an event + * if the speed is higher than 105, this parameter can be set to 1.05. Default multiplier is 1.0. */ - public static final ConfigKey<Boolean> EVENT_OVERSPEED_NOT_REPEAT = new ConfigKey<>( - "event.overspeed.notRepeat", - Collections.singletonList(KeyType.GLOBAL)); + public static final ConfigKey<Double> EVENT_OVERSPEED_THRESHOLD_MULTIPLIER = new DoubleConfigKey( + "event.overspeed.thresholdMultiplier", + List.of(KeyType.CONFIG), + 1.0); /** * Minimal over speed duration to trigger the event. Value in seconds. */ - public static final ConfigKey<Long> EVENT_OVERSPEED_MINIMAL_DURATION = new ConfigKey<>( + public static final ConfigKey<Long> EVENT_OVERSPEED_MINIMAL_DURATION = new LongConfigKey( "event.overspeed.minimalDuration", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Relevant only for geofence speed limits. Use the lowest speed limit from all geofences. */ - public static final ConfigKey<Boolean> EVENT_OVERSPEED_PREFER_LOWEST = new ConfigKey<>( + public static final ConfigKey<Boolean> EVENT_OVERSPEED_PREFER_LOWEST = new BooleanConfigKey( "event.overspeed.preferLowest", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Driver behavior acceleration threshold. Value is in meter per second squared. */ - public static final ConfigKey<Double> EVENT_BEHAVIOR_ACCELERATION_THRESHOLD = new ConfigKey<>( + public static final ConfigKey<Double> EVENT_BEHAVIOR_ACCELERATION_THRESHOLD = new DoubleConfigKey( "event.behavior.accelerationThreshold", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Driver behavior braking threshold. Value is in meter per second squared. */ - public static final ConfigKey<Double> EVENT_BEHAVIOR_BRAKING_THRESHOLD = new ConfigKey<>( + public static final ConfigKey<Double> EVENT_BEHAVIOR_BRAKING_THRESHOLD = new DoubleConfigKey( "event.behavior.brakingThreshold", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Do not generate alert event if same alert was present in last known location. */ - public static final ConfigKey<Boolean> EVENT_IGNORE_DUPLICATE_ALERTS = new ConfigKey<>( + public static final ConfigKey<Boolean> EVENT_IGNORE_DUPLICATE_ALERTS = new BooleanConfigKey( "event.ignoreDuplicateAlerts", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * If set to true, invalid positions will be considered for motion logic. */ - public static final ConfigKey<Boolean> EVENT_MOTION_PROCESS_INVALID_POSITIONS = new ConfigKey<>( + public static final ConfigKey<Boolean> EVENT_MOTION_PROCESS_INVALID_POSITIONS = new BooleanConfigKey( "event.motion.processInvalidPositions", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG, KeyType.DEVICE), + false); /** * If the speed is above specified value, the object is considered to be in motion. Default value is 0.01 knots. */ - public static final ConfigKey<Double> EVENT_MOTION_SPEED_THRESHOLD = new ConfigKey<>( + public static final ConfigKey<Double> EVENT_MOTION_SPEED_THRESHOLD = new DoubleConfigKey( "event.motion.speedThreshold", - Collections.singletonList(KeyType.GLOBAL), + List.of(KeyType.CONFIG, KeyType.DEVICE), 0.01); /** * Global polyline geofence distance. Within that distance from the polyline, point is considered within the * geofence. Each individual geofence can also has 'polylineDistance' attribute which will take precedence. */ - public static final ConfigKey<Double> GEOFENCE_POLYLINE_DISTANCE = new ConfigKey<>( + public static final ConfigKey<Double> GEOFENCE_POLYLINE_DISTANCE = new DoubleConfigKey( "geofence.polylineDistance", - Collections.singletonList(KeyType.GLOBAL), + List.of(KeyType.CONFIG), 25.0); /** + * Enable in-memory database instead of an SQL database. + */ + public static final ConfigKey<Boolean> DATABASE_MEMORY = new BooleanConfigKey( + "database.memory", + List.of(KeyType.CONFIG)); + + /** * Path to the database driver JAR file. Traccar includes drivers for MySQL, PostgreSQL and H2 databases. If you use * one of those, you don't need to specify this parameter. */ - public static final ConfigKey<String> DATABASE_DRIVER_FILE = new ConfigKey<>( + public static final ConfigKey<String> DATABASE_DRIVER_FILE = new StringConfigKey( "database.driverFile", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Database driver Java class. For H2 use 'org.h2.Driver'. MySQL driver class name is 'com.mysql.jdbc.Driver'. */ - public static final ConfigKey<String> DATABASE_DRIVER = new ConfigKey<>( + public static final ConfigKey<String> DATABASE_DRIVER = new StringConfigKey( "database.driver", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Database connection URL. By default Traccar uses H2 database. */ - public static final ConfigKey<String> DATABASE_URL = new ConfigKey<>( + public static final ConfigKey<String> DATABASE_URL = new StringConfigKey( "database.url", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Database user name. Default administrator user for H2 database is 'sa'. */ - public static final ConfigKey<String> DATABASE_USER = new ConfigKey<>( + public static final ConfigKey<String> DATABASE_USER = new StringConfigKey( "database.user", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Database user password. Default password for H2 admin (sa) user is empty. */ - public static final ConfigKey<String> DATABASE_PASSWORD = new ConfigKey<>( + public static final ConfigKey<String> DATABASE_PASSWORD = new StringConfigKey( "database.password", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Path to Liquibase master changelog file. */ - public static final ConfigKey<String> DATABASE_CHANGELOG = new ConfigKey<>( + public static final ConfigKey<String> DATABASE_CHANGELOG = new StringConfigKey( "database.changelog", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Database connection pool size. Default value is defined by the HikariCP library. */ - public static final ConfigKey<Integer> DATABASE_MAX_POOL_SIZE = new ConfigKey<>( + public static final ConfigKey<Integer> DATABASE_MAX_POOL_SIZE = new IntegerConfigKey( "database.maxPoolSize", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * SQL query to check connection status. Default value is 'SELECT 1'. For Oracle database you can use * 'SELECT 1 FROM DUAL'. */ - public static final ConfigKey<String> DATABASE_CHECK_CONNECTION = new ConfigKey<>( + public static final ConfigKey<String> DATABASE_CHECK_CONNECTION = new StringConfigKey( "database.checkConnection", - Collections.singletonList(KeyType.GLOBAL), + List.of(KeyType.CONFIG), "SELECT 1"); /** * Store original HEX or string data as "raw" attribute in the corresponding position. */ - public static final ConfigKey<Boolean> DATABASE_SAVE_ORIGINAL = new ConfigKey<>( + public static final ConfigKey<Boolean> DATABASE_SAVE_ORIGINAL = new BooleanConfigKey( "database.saveOriginal", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** - * By default server syncs with the database if it encounters and unknown device. This flag allows to disable that + * Throttle unknown device database queries when it sends repeated requests. + */ + public static final ConfigKey<Boolean> DATABASE_THROTTLE_UNKNOWN = new BooleanConfigKey( + "database.throttleUnknown", + List.of(KeyType.CONFIG)); + + /** + * By default, server syncs with the database if it encounters and unknown device. This flag allows to disable that * behavior to improve performance in some cases. */ - public static final ConfigKey<Boolean> DATABASE_IGNORE_UNKNOWN = new ConfigKey<>( + public static final ConfigKey<Boolean> DATABASE_IGNORE_UNKNOWN = new BooleanConfigKey( "database.ignoreUnknown", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Automatically register unknown devices in the database. */ - public static final ConfigKey<Boolean> DATABASE_REGISTER_UNKNOWN = new ConfigKey<>( + public static final ConfigKey<Boolean> DATABASE_REGISTER_UNKNOWN = new BooleanConfigKey( "database.registerUnknown", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Default category for auto-registered devices. */ - public static final ConfigKey<String> DATABASE_REGISTER_UNKNOWN_DEFAULT_CATEGORY = new ConfigKey<>( + public static final ConfigKey<String> DATABASE_REGISTER_UNKNOWN_DEFAULT_CATEGORY = new StringConfigKey( "database.registerUnknown.defaultCategory", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * The group id assigned to auto-registered devices. */ - public static final ConfigKey<Long> DATABASE_REGISTER_UNKNOWN_DEFAULT_GROUP_ID = new ConfigKey<>( + public static final ConfigKey<Long> DATABASE_REGISTER_UNKNOWN_DEFAULT_GROUP_ID = new LongConfigKey( "database.registerUnknown.defaultGroupId", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** - * Minimum device refresh timeout in seconds. Default timeout is 5 minutes. + * Automatically register unknown devices with regex filter. */ - public static final ConfigKey<Long> DATABASE_REFRESH_DELAY = new ConfigKey<>( - "database.refreshDelay", - Collections.singletonList(KeyType.GLOBAL), - 300L); + public static final ConfigKey<String> DATABASE_REGISTER_UNKNOWN_REGEX = new StringConfigKey( + "database.registerUnknown.regex", + List.of(KeyType.CONFIG), "\\w{3,15}"); /** * Store empty messages as positions. For example, heartbeats. */ - public static final ConfigKey<Boolean> DATABASE_SAVE_EMPTY = new ConfigKey<>( + public static final ConfigKey<Boolean> DATABASE_SAVE_EMPTY = new BooleanConfigKey( "database.saveEmpty", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Device limit for self registered users. Default value is -1, which indicates no limit. */ - public static final ConfigKey<Integer> USERS_DEFAULT_DEVICE_LIMIT = new ConfigKey<>( + public static final ConfigKey<Integer> USERS_DEFAULT_DEVICE_LIMIT = new IntegerConfigKey( "users.defaultDeviceLimit", - Collections.singletonList(KeyType.GLOBAL), + List.of(KeyType.CONFIG), -1); /** * Default user expiration for self registered users. Value is in days. By default no expiration is set. */ - public static final ConfigKey<Integer> USERS_DEFAULT_EXPIRATION_DAYS = new ConfigKey<>( + public static final ConfigKey<Integer> USERS_DEFAULT_EXPIRATION_DAYS = new IntegerConfigKey( "users.defaultExpirationDays", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** - * LDAP server URL. + * LDAP server URL. For more info check <a href="https://www.traccar.org/ldap/">LDAP config</a>. */ - public static final ConfigKey<String> LDAP_URL = new ConfigKey<>( + public static final ConfigKey<String> LDAP_URL = new StringConfigKey( "ldap.url", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * LDAP server login. */ - public static final ConfigKey<String> LDAP_USER = new ConfigKey<>( + public static final ConfigKey<String> LDAP_USER = new StringConfigKey( "ldap.user", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * LDAP server password. */ - public static final ConfigKey<String> LDAP_PASSWORD = new ConfigKey<>( + public static final ConfigKey<String> LDAP_PASSWORD = new StringConfigKey( "ldap.password", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Force LDAP authentication. */ - public static final ConfigKey<Boolean> LDAP_FORCE = new ConfigKey<>( + public static final ConfigKey<Boolean> LDAP_FORCE = new BooleanConfigKey( "ldap.force", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * LDAP user search base. */ - public static final ConfigKey<String> LDAP_BASE = new ConfigKey<>( + public static final ConfigKey<String> LDAP_BASE = new StringConfigKey( "ldap.base", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * LDAP attribute used as user id. Default value is 'uid'. */ - public static final ConfigKey<String> LDAP_ID_ATTRIBUTE = new ConfigKey<>( + public static final ConfigKey<String> LDAP_ID_ATTRIBUTE = new StringConfigKey( "ldap.idAttribute", - Collections.singletonList(KeyType.GLOBAL), + List.of(KeyType.CONFIG), "uid"); /** * LDAP attribute used as user name. Default value is 'cn'. */ - public static final ConfigKey<String> LDAP_NAME_ATTRIBUTE = new ConfigKey<>( + public static final ConfigKey<String> LDAP_NAME_ATTRIBUTE = new StringConfigKey( "ldap.nameAttribute", - Collections.singletonList(KeyType.GLOBAL), + List.of(KeyType.CONFIG), "cn"); /** * LDAP attribute used as user email. Default value is 'mail'. */ - public static final ConfigKey<String> LDAP_MAIN_ATTRIBUTE = new ConfigKey<>( + public static final ConfigKey<String> LDAP_MAIN_ATTRIBUTE = new StringConfigKey( "ldap.mailAttribute", - Collections.singletonList(KeyType.GLOBAL), + List.of(KeyType.CONFIG), "mail"); /** * LDAP custom search filter. If not specified, '({idAttribute}=:login)' will be used as a filter. */ - public static final ConfigKey<String> LDAP_SEARCH_FILTER = new ConfigKey<>( + public static final ConfigKey<String> LDAP_SEARCH_FILTER = new StringConfigKey( "ldap.searchFilter", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * LDAP custom admin search filter. */ - public static final ConfigKey<String> LDAP_ADMIN_FILTER = new ConfigKey<>( + public static final ConfigKey<String> LDAP_ADMIN_FILTER = new StringConfigKey( "ldap.adminFilter", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * LDAP admin user group. Used if custom admin filter is not specified. */ - public static final ConfigKey<String> LDAP_ADMIN_GROUP = new ConfigKey<>( + public static final ConfigKey<String> LDAP_ADMIN_GROUP = new StringConfigKey( "ldap.adminGroup", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); + + /** + * Force OpenID Connect authentication. When enabled, the Traccar login page will be skipped + * and users are redirected to the OpenID Connect provider. + */ + public static final ConfigKey<Boolean> OPENID_FORCE = new BooleanConfigKey( + "openid.force", + List.of(KeyType.CONFIG)); + + /** + * OpenID Connect Client ID. + * This is a unique ID assigned to each application you register with your identity provider. + * Required to enable SSO. + */ + public static final ConfigKey<String> OPENID_CLIENT_ID = new StringConfigKey( + "openid.clientId", + List.of(KeyType.CONFIG)); + + /** + * OpenID Connect Client Secret. + * This is a secret assigned to each application you register with your identity provider. + * Required to enable SSO. + */ + public static final ConfigKey<String> OPENID_CLIENT_SECRET = new StringConfigKey( + "openid.clientSecret", + List.of(KeyType.CONFIG)); + + /** + * OpenID Connect Issuer (Base) URL. + * This is used to automatically configure the authorization, token and user info URLs if provided. + */ + public static final ConfigKey<String> OPENID_ISSUER_URL = new StringConfigKey( + "openid.issuerUrl", + List.of(KeyType.CONFIG)); + + /** + * OpenID Connect Authorization URL. + * This can usually be found in the documentation of your identity provider or by using the well-known + * configuration endpoint, eg. https://auth.example.com//.well-known/openid-configuration + * Required to enable SSO if openid.issuerUrl is not set. + */ + public static final ConfigKey<String> OPENID_AUTH_URL = new StringConfigKey( + "openid.authUrl", + List.of(KeyType.CONFIG)); + /** + * OpenID Connect Token URL. + * This can be found in the same ways at openid.authUrl. + * Required to enable SSO if openid.issuerUrl is not set. + */ + public static final ConfigKey<String> OPENID_TOKEN_URL = new StringConfigKey( + "openid.tokenUrl", + List.of(KeyType.CONFIG)); + + /** + * OpenID Connect User Info URL. + * This can be found in the same ways at openid.authUrl. + * Required to enable SSO if openid.issuerUrl is not set. + */ + public static final ConfigKey<String> OPENID_USERINFO_URL = new StringConfigKey( + "openid.userInfoUrl", + List.of(KeyType.CONFIG)); + + /** + * OpenID Connect group to restrict access to. + * If this is not provided, all OpenID users will have access to Traccar. + * This option will only work if your OpenID provider supports the groups scope. + */ + public static final ConfigKey<String> OPENID_ALLOW_GROUP = new StringConfigKey( + "openid.allowGroup", + List.of(KeyType.CONFIG)); + + /** + * OpenID Connect group to grant admin access. + * If this is not provided, no groups will be granted admin access. + * This option will only work if your OpenID provider supports the groups scope. + */ + public static final ConfigKey<String> OPENID_ADMIN_GROUP = new StringConfigKey( + "openid.adminGroup", + List.of(KeyType.CONFIG)); /** * If no data is reported by a device for the given amount of time, status changes from online to unknown. Value is * in seconds. Default timeout is 10 minutes. */ - public static final ConfigKey<Long> STATUS_TIMEOUT = new ConfigKey<>( + public static final ConfigKey<Long> STATUS_TIMEOUT = new LongConfigKey( "status.timeout", - Collections.singletonList(KeyType.GLOBAL), + List.of(KeyType.CONFIG), 600L); /** - * Force additional state check when device status changes to 'offline' or 'unknown'. Default false. - */ - public static final ConfigKey<Boolean> STATUS_UPDATE_DEVICE_STATE = new ConfigKey<>( - "status.updateDeviceState", - Collections.singletonList(KeyType.GLOBAL)); - - /** * List of protocol names to ignore offline status. Can be useful to not trigger status change when devices are * configured to disconnect after reporting a batch of data. */ - public static final ConfigKey<String> STATUS_IGNORE_OFFLINE = new ConfigKey<>( + public static final ConfigKey<String> STATUS_IGNORE_OFFLINE = new StringConfigKey( "status.ignoreOffline", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Path to the media folder. Server stores audio, video and photo files in that folder. Sub-folders will be * automatically created for each device by unique id. */ - public static final ConfigKey<String> MEDIA_PATH = new ConfigKey<>( + public static final ConfigKey<String> MEDIA_PATH = new StringConfigKey( "media.path", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Optional parameter to specify network interface for web interface to bind to. By default server will bind to all * available interfaces. */ - public static final ConfigKey<String> WEB_ADDRESS = new ConfigKey<>( + public static final ConfigKey<String> WEB_ADDRESS = new StringConfigKey( "web.address", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Web interface TCP port number. By default Traccar uses port 8082. To avoid specifying port in the browser you * can set it to 80 (default HTTP port). */ - public static final ConfigKey<Integer> WEB_PORT = new ConfigKey<>( + public static final ConfigKey<Integer> WEB_PORT = new IntegerConfigKey( "web.port", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); + + /** + * Maximum API requests per second. Above this limit requests and delayed and throttled. + */ + public static final ConfigKey<Integer> WEB_MAX_REQUESTS_PER_SECOND = new IntegerConfigKey( + "web.maxRequestsPerSec", + List.of(KeyType.CONFIG)); + + /** + * Maximum API request duration in seconds. + */ + public static final ConfigKey<Integer> WEB_MAX_REQUEST_SECONDS = new IntegerConfigKey( + "web.maxRequestSec", + List.of(KeyType.CONFIG), + 600); + + /** + * Sanitize all strings returned via API. This is needed to fix XSS issues in the old web interface. New React-based + * interface doesn't require this. + */ + public static final ConfigKey<Boolean> WEB_SANITIZE = new BooleanConfigKey( + "web.sanitize", + List.of(KeyType.CONFIG)); /** * Path to the web app folder. */ - public static final ConfigKey<String> WEB_PATH = new ConfigKey<>( + public static final ConfigKey<String> WEB_PATH = new StringConfigKey( "web.path", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** - * WebSocket connection timeout in milliseconds. Default timeout is 10 minutes. + * Path to a folder with overrides. It can be used for branding to keep custom logos in a separate place. */ - public static final ConfigKey<Long> WEB_TIMEOUT = new ConfigKey<>( + public static final ConfigKey<String> WEB_OVERRIDE = new StringConfigKey( + "web.override", + List.of(KeyType.CONFIG)); + + /** + * WebSocket connection timeout in milliseconds. Default timeout is 5 minutes. + */ + public static final ConfigKey<Long> WEB_TIMEOUT = new LongConfigKey( "web.timeout", - Collections.singletonList(KeyType.GLOBAL), - 60000L); + List.of(KeyType.CONFIG), + 300000L); /** * Authentication sessions timeout in seconds. By default no timeout. */ - public static final ConfigKey<Integer> WEB_SESSION_TIMEOUT = new ConfigKey<>( + public static final ConfigKey<Integer> WEB_SESSION_TIMEOUT = new IntegerConfigKey( "web.sessionTimeout", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Enable database access console via '/console' URL. Use only for debugging. Never use in production. */ - public static final ConfigKey<Boolean> WEB_CONSOLE = new ConfigKey<>( + public static final ConfigKey<Boolean> WEB_CONSOLE = new BooleanConfigKey( "web.console", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Server debug version of the web app. Not recommended to use for performance reasons. It is intended to be used * for development and debugging purposes. */ - public static final ConfigKey<Boolean> WEB_DEBUG = new ConfigKey<>( + public static final ConfigKey<Boolean> WEB_DEBUG = new BooleanConfigKey( "web.debug", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); + + /** + * A token to login as a virtual admin account. Can be used to restore access in case of issues with regular admin + * login. For example, if password is lost and can't be restored. + */ + public static final ConfigKey<String> WEB_SERVICE_ACCOUNT_TOKEN = new StringConfigKey( + "web.serviceAccountToken", + List.of(KeyType.CONFIG)); /** * Cross-origin resource sharing origin header value. */ - public static final ConfigKey<String> WEB_ORIGIN = new ConfigKey<>( + public static final ConfigKey<String> WEB_ORIGIN = new StringConfigKey( "web.origin", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Cache control header value. By default resources are cached for one hour. */ - public static final ConfigKey<String> WEB_CACHE_CONTROL = new ConfigKey<>( + public static final ConfigKey<String> WEB_CACHE_CONTROL = new StringConfigKey( "web.cacheControl", - Collections.singletonList(KeyType.GLOBAL), + List.of(KeyType.CONFIG), "max-age=3600,public"); /** - * URL to forward positions. Data is passed through URL parameters. For example, {uniqueId} for device identifier, - * {latitude} and {longitude} for coordinates. + * Host for raw data forwarding. */ - public static final ConfigKey<String> FORWARD_URL = new ConfigKey<>( - "forward.url", - Collections.singletonList(KeyType.GLOBAL)); + public static final ConfigKey<String> SERVER_FORWARD = new StringConfigKey( + "server.forward", + List.of(KeyType.CONFIG)); /** - * Additional HTTP header, can be used for authorization. + * Position forwarding format. Available options are "url", "json" and "kafka". Default is "url". */ - public static final ConfigKey<String> FORWARD_HEADER = new ConfigKey<>( - "forward.header", - Collections.singletonList(KeyType.GLOBAL)); + public static final ConfigKey<String> FORWARD_TYPE = new StringConfigKey( + "forward.type", + List.of(KeyType.CONFIG), + "url"); + + /** + * Position forwarding AMQP exchange. + */ + public static final ConfigKey<String> FORWARD_EXCHANGE = new StringConfigKey( + "forward.exchange", + List.of(KeyType.CONFIG), + "traccar"); /** - * Boolean value to enable forwarding in JSON format. + * Position forwarding Kafka topic or AQMP Routing Key. */ - public static final ConfigKey<Boolean> FORWARD_JSON = new ConfigKey<>( - "forward.json", - Collections.singletonList(KeyType.GLOBAL)); + public static final ConfigKey<String> FORWARD_TOPIC = new StringConfigKey( + "forward.topic", + List.of(KeyType.CONFIG), + "positions"); /** - * Boolean value to enable URL parameters in json mode. For example, {uniqueId} for device identifier, + * URL to forward positions. Data is passed through URL parameters. For example, {uniqueId} for device identifier, * {latitude} and {longitude} for coordinates. */ - public static final ConfigKey<Boolean> FORWARD_URL_VARIABLES = new ConfigKey<>( - "forward.urlVariables", - Collections.singletonList(KeyType.GLOBAL)); + public static final ConfigKey<String> FORWARD_URL = new StringConfigKey( + "forward.url", + List.of(KeyType.CONFIG)); + + /** + * Additional HTTP header, can be used for authorization. + */ + public static final ConfigKey<String> FORWARD_HEADER = new StringConfigKey( + "forward.header", + List.of(KeyType.CONFIG)); /** * Position forwarding retrying enable. When enabled, additional attempts are made to deliver positions. If initial @@ -641,589 +890,808 @@ public final class Keys { * If forwarding is retried for 'forward.retry.count', retrying is canceled and the position is dropped. Positions * pending to be delivered are limited to 'forward.retry.limit'. If this limit is reached, positions get discarded. */ - public static final ConfigKey<Boolean> FORWARD_RETRY_ENABLE = new ConfigKey<>( + public static final ConfigKey<Boolean> FORWARD_RETRY_ENABLE = new BooleanConfigKey( "forward.retry.enable", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Position forwarding retry first delay in milliseconds. * Can be set to anything greater than 0. Defaults to 100 milliseconds. */ - public static final ConfigKey<Integer> FORWARD_RETRY_DELAY = new ConfigKey<>( + public static final ConfigKey<Integer> FORWARD_RETRY_DELAY = new IntegerConfigKey( "forward.retry.delay", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG), + 100); /** * Position forwarding retry maximum retries. * Can be set to anything greater than 0. Defaults to 10 retries. */ - public static final ConfigKey<Integer> FORWARD_RETRY_COUNT = new ConfigKey<>( + public static final ConfigKey<Integer> FORWARD_RETRY_COUNT = new IntegerConfigKey( "forward.retry.count", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG), + 10); /** * Position forwarding retry pending positions limit. * Can be set to anything greater than 0. Defaults to 100 positions. */ - public static final ConfigKey<Integer> FORWARD_RETRY_LIMIT = new ConfigKey<>( + public static final ConfigKey<Integer> FORWARD_RETRY_LIMIT = new IntegerConfigKey( "forward.retry.limit", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG), + 100); + + /** + * Events forwarding format. Available options are "json" and "kafka". Default is "json". + */ + public static final ConfigKey<String> EVENT_FORWARD_TYPE = new StringConfigKey( + "event.forward.type", + List.of(KeyType.CONFIG), + "json"); + + /** + * Events forwarding AMQP exchange. + */ + public static final ConfigKey<String> EVENT_FORWARD_EXCHANGE = new StringConfigKey( + "event.forward.exchange", + List.of(KeyType.CONFIG), + "traccar"); + + /** + * Events forwarding Kafka topic or AQMP Routing Key. + */ + public static final ConfigKey<String> EVENT_FORWARD_TOPIC = new StringConfigKey( + "event.forward.topic", + List.of(KeyType.CONFIG), + "events"); /** * Events forwarding URL. */ - public static final ConfigKey<String> EVENT_FORWARD_URL = new ConfigKey<>( + public static final ConfigKey<String> EVENT_FORWARD_URL = new StringConfigKey( "event.forward.url", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Events forwarding headers. Example value: * FirstHeader: hello * SecondHeader: world */ - public static final ConfigKey<String> EVENT_FORWARD_HEADERS = new ConfigKey<>( + public static final ConfigKey<String> EVENT_FORWARD_HEADERS = new StringConfigKey( "event.forward.header", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** - * Enable commands queuing when devices are offline. Commands are buffered in memory only, so restarting service - * will clear the buffer. + * Root folder for all template files. */ - public static final ConfigKey<Boolean> COMMANDS_QUEUEING = new ConfigKey<>( - "commands.queueing", - Collections.singletonList(KeyType.GLOBAL)); + public static final ConfigKey<String> TEMPLATES_ROOT = new StringConfigKey( + "templates.root", + List.of(KeyType.CONFIG), + "templates"); + + /** + * Log emails instead of sending them via SMTP. Intended for testing purposes only. + */ + public static final ConfigKey<Boolean> MAIL_DEBUG = new BooleanConfigKey( + "mail.debug", + List.of(KeyType.CONFIG)); + + /** + * Restrict global SMTP configuration to system messages only (e.g. password reset). + */ + public static final ConfigKey<Boolean> MAIL_SMTP_SYSTEM_ONLY = new BooleanConfigKey( + "mail.smtp.systemOnly", + List.of(KeyType.CONFIG)); + + /** + * Force SMTP settings from the config file and ignore user attributes. + */ + public static final ConfigKey<Boolean> MAIL_SMTP_IGNORE_USER_CONFIG = new BooleanConfigKey( + "mail.smtp.ignoreUserConfig", + List.of(KeyType.CONFIG)); + + /** + * The SMTP server to connect to. + */ + public static final ConfigKey<String> MAIL_SMTP_HOST = new StringConfigKey( + "mail.smtp.host", + List.of(KeyType.CONFIG, KeyType.USER)); + + /** + * The SMTP server port to connect. Defaults to 25. + */ + public static final ConfigKey<Integer> MAIL_SMTP_PORT = new IntegerConfigKey( + "mail.smtp.port", + List.of(KeyType.CONFIG, KeyType.USER), + 25); + + /** + * Email transport protocol. Default value is "smtp". + */ + public static final ConfigKey<String> MAIL_TRANSPORT_PROTOCOL = new StringConfigKey( + "mail.transport.protocol", + List.of(KeyType.CONFIG, KeyType.USER), + "smtp"); + + /** + * If true, enables the use of the STARTTLS command (if supported by the server) to switch the connection to a + * TLS-protected connection before issuing any login commands. + */ + public static final ConfigKey<Boolean> MAIL_SMTP_STARTTLS_ENABLE = new BooleanConfigKey( + "mail.smtp.starttls.enable", + List.of(KeyType.CONFIG, KeyType.USER)); + + /** + * If true, requires the use of the STARTTLS command. If the server doesn't support the STARTTLS command, or the + * command fails, the connect method will fail. + */ + public static final ConfigKey<Boolean> MAIL_SMTP_STARTTLS_REQUIRED = new BooleanConfigKey( + "mail.smtp.starttls.required", + List.of(KeyType.CONFIG, KeyType.USER)); + + /** + * If set to true, use SSL to connect and use the SSL port by default. + */ + public static final ConfigKey<Boolean> MAIL_SMTP_SSL_ENABLE = new BooleanConfigKey( + "mail.smtp.ssl.enable", + List.of(KeyType.CONFIG, KeyType.USER)); + + /** + * If set to "*", all hosts are trusted. If set to a whitespace separated list of hosts, those hosts are trusted. + * Otherwise, trust depends on the certificate the server presents. + */ + public static final ConfigKey<String> MAIL_SMTP_SSL_TRUST = new StringConfigKey( + "mail.smtp.ssl.trust", + List.of(KeyType.CONFIG, KeyType.USER)); + + /** + * Specifies the SSL protocols that will be enabled for SSL connections. + */ + public static final ConfigKey<String> MAIL_SMTP_SSL_PROTOCOLS = new StringConfigKey( + "mail.smtp.ssl.protocols", + List.of(KeyType.CONFIG, KeyType.USER)); + + /** + * SMTP connection username. + */ + public static final ConfigKey<String> MAIL_SMTP_USERNAME = new StringConfigKey( + "mail.smtp.username", + List.of(KeyType.CONFIG, KeyType.USER)); + + /** + * SMTP connection password. + */ + public static final ConfigKey<String> MAIL_SMTP_PASSWORD = new StringConfigKey( + "mail.smtp.password", + List.of(KeyType.CONFIG, KeyType.USER)); + + /** + * Email address to use for SMTP MAIL command. + */ + public static final ConfigKey<String> MAIL_SMTP_FROM = new StringConfigKey( + "mail.smtp.from", + List.of(KeyType.CONFIG, KeyType.USER)); + + /** + * The personal name for the email from address. + */ + public static final ConfigKey<String> MAIL_SMTP_FROM_NAME = new StringConfigKey( + "mail.smtp.fromName", + List.of(KeyType.CONFIG, KeyType.USER)); /** * SMS API service full URL. Enables SMS commands and notifications. */ - public static final ConfigKey<String> SMS_HTTP_URL = new ConfigKey<>( + public static final ConfigKey<String> SMS_HTTP_URL = new StringConfigKey( "sms.http.url", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * SMS API authorization header name. Default value is 'Authorization'. */ - public static final ConfigKey<String> SMS_HTTP_AUTHORIZATION_HEADER = new ConfigKey<>( + public static final ConfigKey<String> SMS_HTTP_AUTHORIZATION_HEADER = new StringConfigKey( "sms.http.authorizationHeader", - Collections.singletonList(KeyType.GLOBAL), + List.of(KeyType.CONFIG), "Authorization"); /** * SMS API authorization header value. This value takes precedence over user and password. */ - public static final ConfigKey<String> SMS_HTTP_AUTHORIZATION = new ConfigKey<>( + public static final ConfigKey<String> SMS_HTTP_AUTHORIZATION = new StringConfigKey( "sms.http.authorization", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * SMS API basic authentication user. */ - public static final ConfigKey<String> SMS_HTTP_USER = new ConfigKey<>( + public static final ConfigKey<String> SMS_HTTP_USER = new StringConfigKey( "sms.http.user", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * SMS API basic authentication password. */ - public static final ConfigKey<String> SMS_HTTP_PASSWORD = new ConfigKey<>( + public static final ConfigKey<String> SMS_HTTP_PASSWORD = new StringConfigKey( "sms.http.password", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * SMS API body template. Placeholders {phone} and {message} can be used in the template. * If value starts with '{' or '[', server automatically assumes JSON format. */ - public static final ConfigKey<String> SMS_HTTP_TEMPLATE = new ConfigKey<>( + public static final ConfigKey<String> SMS_HTTP_TEMPLATE = new StringConfigKey( "sms.http.template", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * AWS Access Key with SNS permission. */ - public static final ConfigKey<String> SMS_AWS_ACCESS = new ConfigKey<>( + public static final ConfigKey<String> SMS_AWS_ACCESS = new StringConfigKey( "sms.aws.access", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * AWS Secret Access Key with SNS permission. */ - public static final ConfigKey<String> SMS_AWS_SECRET = new ConfigKey<>( + public static final ConfigKey<String> SMS_AWS_SECRET = new StringConfigKey( "sms.aws.secret", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * AWS Region for SNS service. * Make sure to use regions that are supported for messaging. */ - public static final ConfigKey<String> SMS_AWS_REGION = new ConfigKey<>( + public static final ConfigKey<String> SMS_AWS_REGION = new StringConfigKey( "sms.aws.region", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); + + /** + * Enabled notification options. Comma-separated string is expected. + * Example: web,mail,sms + */ + public static final ConfigKey<String> NOTIFICATOR_TYPES = new StringConfigKey( + "notificator.types", + List.of(KeyType.CONFIG)); /** * Traccar notification API key. */ - public static final ConfigKey<String> NOTIFICATOR_TRACCAR_KEY = new ConfigKey<>( + public static final ConfigKey<String> NOTIFICATOR_TRACCAR_KEY = new StringConfigKey( "notificator.traccar.key", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** - * Firebase server API key for push notifications. + * Firebase service account JSON. */ - public static final ConfigKey<String> NOTIFICATOR_FIREBASE_KEY = new ConfigKey<>( - "notificator.firebase.key", - Collections.singletonList(KeyType.GLOBAL)); + public static final ConfigKey<String> NOTIFICATOR_FIREBASE_SERVICE_ACCOUNT = new StringConfigKey( + "notificator.firebase.serviceAccount", + List.of(KeyType.CONFIG)); /** * Pushover notification user name. */ - public static final ConfigKey<String> NOTIFICATOR_PUSHOVER_USER = new ConfigKey<>( + public static final ConfigKey<String> NOTIFICATOR_PUSHOVER_USER = new StringConfigKey( "notificator.pushover.user", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Pushover notification user token. */ - public static final ConfigKey<String> NOTIFICATOR_PUSHOVER_TOKEN = new ConfigKey<>( + public static final ConfigKey<String> NOTIFICATOR_PUSHOVER_TOKEN = new StringConfigKey( "notificator.pushover.token", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Telegram notification API key. */ - public static final ConfigKey<String> NOTIFICATOR_TELEGRAM_KEY = new ConfigKey<>( + public static final ConfigKey<String> NOTIFICATOR_TELEGRAM_KEY = new StringConfigKey( "notificator.telegram.key", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Telegram notification chat id to post messages to. */ - public static final ConfigKey<String> NOTIFICATOR_TELEGRAM_CHAT_ID = new ConfigKey<>( + public static final ConfigKey<String> NOTIFICATOR_TELEGRAM_CHAT_ID = new StringConfigKey( "notificator.telegram.chatId", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Telegram notification send location message. */ - public static final ConfigKey<Boolean> NOTIFICATOR_TELEGRAM_SEND_LOCATION = new ConfigKey<>( + public static final ConfigKey<Boolean> NOTIFICATOR_TELEGRAM_SEND_LOCATION = new BooleanConfigKey( "notificator.telegram.sendLocation", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Maximum time period for reports in seconds. Can be useful to prevent users to request unreasonably long reports. * By default there is no limit. */ - public static final ConfigKey<Long> REPORT_PERIOD_LIMIT = new ConfigKey<>( + public static final ConfigKey<Long> REPORT_PERIOD_LIMIT = new LongConfigKey( "report.periodLimit", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); + + /** + * Time threshold for fast reports. Fast reports are more efficient, but less accurate and missing some information. + * The value is in seconds. One day by default. + */ + public static final ConfigKey<Long> REPORT_FAST_THRESHOLD = new LongConfigKey( + "report.fastThreshold", + List.of(KeyType.CONFIG), + 86400L); /** * Trips less than minimal duration and minimal distance are ignored. 300 seconds and 500 meters are default. */ - public static final ConfigKey<Long> REPORT_TRIP_MINIMAL_TRIP_DISTANCE = new ConfigKey<>( + public static final ConfigKey<Long> REPORT_TRIP_MINIMAL_TRIP_DISTANCE = new LongConfigKey( "report.trip.minimalTripDistance", - Collections.singletonList(KeyType.GLOBAL), + List.of(KeyType.CONFIG, KeyType.DEVICE), 500L); /** * Trips less than minimal duration and minimal distance are ignored. 300 seconds and 500 meters are default. */ - public static final ConfigKey<Long> REPORT_TRIP_MINIMAL_TRIP_DURATION = new ConfigKey<>( + public static final ConfigKey<Long> REPORT_TRIP_MINIMAL_TRIP_DURATION = new LongConfigKey( "report.trip.minimalTripDuration", - Collections.singletonList(KeyType.GLOBAL), + List.of(KeyType.CONFIG, KeyType.DEVICE), 300L); /** * Parking less than minimal duration does not cut trip. Default 300 seconds. */ - public static final ConfigKey<Long> REPORT_TRIP_MINIMAL_PARKING_DURATION = new ConfigKey<>( + public static final ConfigKey<Long> REPORT_TRIP_MINIMAL_PARKING_DURATION = new LongConfigKey( "report.trip.minimalParkingDuration", - Collections.singletonList(KeyType.GLOBAL), + List.of(KeyType.CONFIG, KeyType.DEVICE), 300L); /** * Gaps of more than specified time are counted as stops. Default value is one hour. */ - public static final ConfigKey<Long> REPORT_TRIP_MINIMAL_NO_DATA_DURATION = new ConfigKey<>( + public static final ConfigKey<Long> REPORT_TRIP_MINIMAL_NO_DATA_DURATION = new LongConfigKey( "report.trip.minimalNoDataDuration", - Collections.singletonList(KeyType.GLOBAL), + List.of(KeyType.CONFIG, KeyType.DEVICE), 3600L); /** * Flag to enable ignition use for trips calculation. */ - public static final ConfigKey<Boolean> REPORT_TRIP_USE_IGNITION = new ConfigKey<>( + public static final ConfigKey<Boolean> REPORT_TRIP_USE_IGNITION = new BooleanConfigKey( "report.trip.useIgnition", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG, KeyType.DEVICE), + false); + + /** + * Ignore odometer value reported by the device and use server-calculated total distance instead. This is useful + * if device reports invalid or zero odometer values. + */ + public static final ConfigKey<Boolean> REPORT_IGNORE_ODOMETER = new BooleanConfigKey( + "report.ignoreOdometer", + List.of(KeyType.CONFIG), + false); /** * Boolean flag to enable or disable position filtering. */ - public static final ConfigKey<Boolean> FILTER_ENABLE = new ConfigKey<>( + public static final ConfigKey<Boolean> FILTER_ENABLE = new BooleanConfigKey( "filter.enable", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Filter invalid (valid field is set to false) positions. */ - public static final ConfigKey<Boolean> FILTER_INVALID = new ConfigKey<>( + public static final ConfigKey<Boolean> FILTER_INVALID = new BooleanConfigKey( "filter.invalid", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Filter zero coordinates. Zero latitude and longitude are theoretically valid values, but it practice it usually * indicates invalid GPS data. */ - public static final ConfigKey<Boolean> FILTER_ZERO = new ConfigKey<>( + public static final ConfigKey<Boolean> FILTER_ZERO = new BooleanConfigKey( "filter.zero", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Filter duplicate records (duplicates are detected by time value). */ - public static final ConfigKey<Boolean> FILTER_DUPLICATE = new ConfigKey<>( + public static final ConfigKey<Boolean> FILTER_DUPLICATE = new BooleanConfigKey( "filter.duplicate", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); + + /** + * Filter messages that do not have GPS location. If they are not filtered, they will include the last known + * location. + */ + public static final ConfigKey<Boolean> FILTER_OUTDATED = new BooleanConfigKey( + "filter.outdated", + List.of(KeyType.CONFIG)); /** - * Filter records with fix time in future. The values is specified in seconds. Records that have fix time more than - * specified number of seconds later than current server time would be filtered out. + * Filter records with fix time in the future. The value is specified in seconds. Records that have fix time more + * than the specified number of seconds later than current server time would be filtered out. */ - public static final ConfigKey<Long> FILTER_FUTURE = new ConfigKey<>( + public static final ConfigKey<Long> FILTER_FUTURE = new LongConfigKey( "filter.future", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); + + /** + * Filter records with fix time in the past. The value is specified in seconds. Records that have fix time more + * than the specified number of seconds before current server time would be filtered out. + */ + public static final ConfigKey<Long> FILTER_PAST = new LongConfigKey( + "filter.past", + List.of(KeyType.CONFIG)); /** * Filter positions with accuracy less than specified value in meters. */ - public static final ConfigKey<Integer> FILTER_ACCURACY = new ConfigKey<>( + public static final ConfigKey<Integer> FILTER_ACCURACY = new IntegerConfigKey( "filter.accuracy", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Filter cell and wifi locations that are coming from geolocation provider. */ - public static final ConfigKey<Boolean> FILTER_APPROXIMATE = new ConfigKey<>( + public static final ConfigKey<Boolean> FILTER_APPROXIMATE = new BooleanConfigKey( "filter.approximate", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Filter positions with exactly zero speed values. */ - public static final ConfigKey<Boolean> FILTER_STATIC = new ConfigKey<>( + public static final ConfigKey<Boolean> FILTER_STATIC = new BooleanConfigKey( "filter.static", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Filter records by distance. The values is specified in meters. If the new position is less far than this value * from the last one it gets filtered out. */ - public static final ConfigKey<Integer> FILTER_DISTANCE = new ConfigKey<>( + public static final ConfigKey<Integer> FILTER_DISTANCE = new IntegerConfigKey( "filter.distance", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Filter records by Maximum Speed value in knots. Can be used to filter jumps to far locations even if Position * appears valid or if Position `speed` field reported by the device is also within limits. Calculates speed from * the distance to the previous position and the elapsed time. - * * Tip: Shouldn't be too low. Start testing with values at about 25000. */ - public static final ConfigKey<Integer> FILTER_MAX_SPEED = new ConfigKey<>( + public static final ConfigKey<Integer> FILTER_MAX_SPEED = new IntegerConfigKey( "filter.maxSpeed", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Filter position if time from previous position is less than specified value in seconds. */ - public static final ConfigKey<Integer> FILTER_MIN_PERIOD = new ConfigKey<>( + public static final ConfigKey<Integer> FILTER_MIN_PERIOD = new IntegerConfigKey( "filter.minPeriod", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); + + /** + * Filter position if the daily limit is exceeded for the device. + */ + public static final ConfigKey<Integer> FILTER_DAILY_LIMIT = new IntegerConfigKey( + "filter.dailyLimit", + List.of(KeyType.CONFIG)); /** * If false, the server expects all locations to come sequentially (for each device). Filter checks for duplicates, * distance, speed, or time period only against the location that was last received by server. - * * If true, the server expects locations to come at random order (since tracking device might go offline). * Filter checks for duplicates, distance, speed, or time period against the preceding Position's. * Important: setting to true can cause potential performance issues. */ - public static final ConfigKey<Boolean> FILTER_RELATIVE = new ConfigKey<>( + public static final ConfigKey<Boolean> FILTER_RELATIVE = new BooleanConfigKey( "filter.relative", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Time limit for the filtering in seconds. If the time difference between the last position was received by server * and a new position is received by server is more than this limit, the new position will not be filtered out. */ - public static final ConfigKey<Long> FILTER_SKIP_LIMIT = new ConfigKey<>( + public static final ConfigKey<Long> FILTER_SKIP_LIMIT = new LongConfigKey( "filter.skipLimit", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Enable attributes skipping. Attribute skipping can be enabled in the config or device attributes. * If position contains any attribute mentioned in "filter.skipAttributes" config key, position is not filtered out. */ - public static final ConfigKey<Boolean> FILTER_SKIP_ATTRIBUTES_ENABLE = new ConfigKey<>( + public static final ConfigKey<Boolean> FILTER_SKIP_ATTRIBUTES_ENABLE = new BooleanConfigKey( "filter.skipAttributes.enable", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); + + /** + * Attribute skipping can be enabled in the config or device attributes. + * If position contains any attribute mentioned in "filter.skipAttributes" config key, position is not filtered out. + */ + public static final ConfigKey<String> FILTER_SKIP_ATTRIBUTES = new StringConfigKey( + "filter.skipAttributes", + List.of(KeyType.CONFIG, KeyType.DEVICE), + ""); /** * Override device time. Possible values are 'deviceTime' and 'serverTime' */ - public static final ConfigKey<String> TIME_OVERRIDE = new ConfigKey<>( + public static final ConfigKey<String> TIME_OVERRIDE = new StringConfigKey( "time.override", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); + + /** + * List of protocols to enable. If not specified, Traccar enabled all protocols that have port numbers listed. + * The value is a comma-separated list of protocol names. + * Example value: teltonika,osmand + */ + public static final ConfigKey<String> PROTOCOLS_ENABLE = new StringConfigKey( + "protocols.enable", + List.of(KeyType.CONFIG)); /** * List of protocols for overriding time. If not specified override is applied globally. List consist of protocol * names that can be separated by comma or single space character. */ - public static final ConfigKey<String> TIME_PROTOCOLS = new ConfigKey<>( + public static final ConfigKey<String> TIME_PROTOCOLS = new StringConfigKey( "time.protocols", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Replaces coordinates with last known if change is less than a 'coordinates.minError' meters * or more than a 'coordinates.maxError' meters. Helps to avoid coordinates jumps during parking period * or jumps to zero coordinates. */ - public static final ConfigKey<Boolean> COORDINATES_FILTER = new ConfigKey<>( + public static final ConfigKey<Boolean> COORDINATES_FILTER = new BooleanConfigKey( "coordinates.filter", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Distance in meters. Distances below this value gets handled like explained in 'coordinates.filter'. */ - public static final ConfigKey<Integer> COORDINATES_MIN_ERROR = new ConfigKey<>( + public static final ConfigKey<Integer> COORDINATES_MIN_ERROR = new IntegerConfigKey( "coordinates.minError", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Distance in meters. Distances above this value gets handled like explained in 'coordinates.filter', but only if * Position is also marked as 'invalid'. */ - public static final ConfigKey<Integer> COORDINATES_MAX_ERROR = new ConfigKey<>( + public static final ConfigKey<Integer> COORDINATES_MAX_ERROR = new IntegerConfigKey( "coordinates.maxError", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Enable to save device IP addresses information. Disabled by default. */ - public static final ConfigKey<Boolean> PROCESSING_REMOTE_ADDRESS_ENABLE = new ConfigKey<>( + public static final ConfigKey<Boolean> PROCESSING_REMOTE_ADDRESS_ENABLE = new BooleanConfigKey( "processing.remoteAddress.enable", - Collections.singletonList(KeyType.GLOBAL)); - - /** - * Enable engine hours calculation on the server side. It uses ignition value to determine engine state. - */ - public static final ConfigKey<Boolean> PROCESSING_ENGINE_HOURS_ENABLE = new ConfigKey<>( - "processing.engineHours.enable", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Enable copying of missing attributes from last position to the current one. Might be useful if device doesn't * send some values in every message. */ - public static final ConfigKey<Boolean> PROCESSING_COPY_ATTRIBUTES_ENABLE = new ConfigKey<>( + public static final ConfigKey<Boolean> PROCESSING_COPY_ATTRIBUTES_ENABLE = new BooleanConfigKey( "processing.copyAttributes.enable", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** - * Enable computed attributes processing. + * List of attributes to copy. Attributes should be separated by a comma without any spacing. + * For example: alarm,ignition */ - public static final ConfigKey<Boolean> PROCESSING_COMPUTED_ATTRIBUTES_ENABLE = new ConfigKey<>( - "processing.computedAttributes.enable", - Collections.singletonList(KeyType.GLOBAL)); + public static final ConfigKey<String> PROCESSING_COPY_ATTRIBUTES = new StringConfigKey( + "processing.copyAttributes", + List.of(KeyType.CONFIG, KeyType.DEVICE)); /** * Enable computed attributes processing. */ - public static final ConfigKey<Boolean> PROCESSING_COMPUTED_ATTRIBUTES_DEVICE_ATTRIBUTES = new ConfigKey<>( + public static final ConfigKey<Boolean> PROCESSING_COMPUTED_ATTRIBUTES_DEVICE_ATTRIBUTES = new BooleanConfigKey( "processing.computedAttributes.deviceAttributes", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); + + /** + * Enable local variables declaration. + */ + public static final ConfigKey<Boolean> PROCESSING_COMPUTED_ATTRIBUTES_LOCAL_VARIABLES = new BooleanConfigKey( + "processing.computedAttributes.localVariables", + List.of(KeyType.CONFIG)); + + /** + * Enable loops processing. + */ + public static final ConfigKey<Boolean> PROCESSING_COMPUTED_ATTRIBUTES_LOOPS = new BooleanConfigKey( + "processing.computedAttributes.loops", + List.of(KeyType.CONFIG)); + + /** + * Enable new instances creation. + * When disabled, parsing a script/expression using 'new(...)' will throw a parsing exception; + */ + public static final ConfigKey<Boolean> PROCESSING_COMPUTED_ATTRIBUTES_NEW_INSTANCE_CREATION = new BooleanConfigKey( + "processing.computedAttributes.newInstanceCreation", + List.of(KeyType.CONFIG)); /** * Boolean flag to enable or disable reverse geocoder. */ - public static final ConfigKey<Boolean> GEOCODER_ENABLE = new ConfigKey<>( + public static final ConfigKey<Boolean> GEOCODER_ENABLE = new BooleanConfigKey( "geocoder.enable", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Reverse geocoder type. Check reverse geocoding documentation for more info. By default (if the value is not * specified) server uses Google API. */ - public static final ConfigKey<String> GEOCODER_TYPE = new ConfigKey<>( + public static final ConfigKey<String> GEOCODER_TYPE = new StringConfigKey( "geocoder.type", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Geocoder server URL. Applicable only to Nominatim and Gisgraphy providers. */ - public static final ConfigKey<String> GEOCODER_URL = new ConfigKey<>( + public static final ConfigKey<String> GEOCODER_URL = new StringConfigKey( "geocoder.url", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * App id for use with Here provider. */ - public static final ConfigKey<String> GEOCODER_ID = new ConfigKey<>( + public static final ConfigKey<String> GEOCODER_ID = new StringConfigKey( "geocoder.id", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Provider API key. Most providers require API keys. */ - public static final ConfigKey<String> GEOCODER_KEY = new ConfigKey<>( + public static final ConfigKey<String> GEOCODER_KEY = new StringConfigKey( "geocoder.key", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Language parameter for providers that support localization (e.g. Google and Nominatim). */ - public static final ConfigKey<String> GEOCODER_LANGUAGE = new ConfigKey<>( + public static final ConfigKey<String> GEOCODER_LANGUAGE = new StringConfigKey( "geocoder.language", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Address format string. Default value is %h %r, %t, %s, %c. See AddressFormat for more info. */ - public static final ConfigKey<String> GEOCODER_FORMAT = new ConfigKey<>( + public static final ConfigKey<String> GEOCODER_FORMAT = new StringConfigKey( "geocoder.format", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Cache size for geocoding results. */ - public static final ConfigKey<Integer> GEOCODER_CACHE_SIZE = new ConfigKey<>( + public static final ConfigKey<Integer> GEOCODER_CACHE_SIZE = new IntegerConfigKey( "geocoder.cacheSize", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Disable automatic reverse geocoding requests for all positions. */ - public static final ConfigKey<Boolean> GEOCODER_IGNORE_POSITIONS = new ConfigKey<>( + public static final ConfigKey<Boolean> GEOCODER_IGNORE_POSITIONS = new BooleanConfigKey( "geocoder.ignorePositions", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Boolean flag to apply reverse geocoding to invalid positions. */ - public static final ConfigKey<Boolean> GEOCODER_PROCESS_INVALID_POSITIONS = new ConfigKey<>( + public static final ConfigKey<Boolean> GEOCODER_PROCESS_INVALID_POSITIONS = new BooleanConfigKey( "geocoder.processInvalidPositions", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Optional parameter to specify minimum distance for new reverse geocoding request. If distance is less than * specified value (in meters), then Traccar will reuse last known address. */ - public static final ConfigKey<Integer> GEOCODER_REUSE_DISTANCE = new ConfigKey<>( + public static final ConfigKey<Integer> GEOCODER_REUSE_DISTANCE = new IntegerConfigKey( "geocoder.reuseDistance", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Perform geocoding when preparing reports and sending notifications. */ - public static final ConfigKey<Boolean> GEOCODER_ON_REQUEST = new ConfigKey<>( + public static final ConfigKey<Boolean> GEOCODER_ON_REQUEST = new BooleanConfigKey( "geocoder.onRequest", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Boolean flag to enable LBS location resolution. Some devices send cell towers information and WiFi point when GPS * location is not available. Traccar can determine coordinates based on that information using third party * services. Default value is false. */ - public static final ConfigKey<Boolean> GEOLOCATION_ENABLE = new ConfigKey<>( + public static final ConfigKey<Boolean> GEOLOCATION_ENABLE = new BooleanConfigKey( "geolocation.enable", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Provider to use for LBS location. Available options: google, mozilla and opencellid. By default opencellid is * used. You have to supply a key that you get from corresponding provider. For more information see LBS geolocation * documentation. */ - public static final ConfigKey<String> GEOLOCATION_TYPE = new ConfigKey<>( + public static final ConfigKey<String> GEOLOCATION_TYPE = new StringConfigKey( "geolocation.type", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Geolocation provider API URL address. Not required for most providers. */ - public static final ConfigKey<String> GEOLOCATION_URL = new ConfigKey<>( + public static final ConfigKey<String> GEOLOCATION_URL = new StringConfigKey( "geolocation.url", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Provider API key. OpenCellID service requires API key. */ - public static final ConfigKey<String> GEOLOCATION_KEY = new ConfigKey<>( + public static final ConfigKey<String> GEOLOCATION_KEY = new StringConfigKey( "geolocation.key", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Boolean flag to apply geolocation to invalid positions. */ - public static final ConfigKey<Boolean> GEOLOCATION_PROCESS_INVALID_POSITIONS = new ConfigKey<>( + public static final ConfigKey<Boolean> GEOLOCATION_PROCESS_INVALID_POSITIONS = new BooleanConfigKey( "geolocation.processInvalidPositions", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); + + /** + * Reuse last geolocation result if network details have not changed. + */ + public static final ConfigKey<Boolean> GEOLOCATION_REUSE = new BooleanConfigKey( + "geolocation.reuse", + List.of(KeyType.CONFIG)); /** * Default MCC value to use if device doesn't report MCC. */ - public static final ConfigKey<Integer> GEOLOCATION_MCC = new ConfigKey<>( + public static final ConfigKey<Integer> GEOLOCATION_MCC = new IntegerConfigKey( "geolocation.mcc", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Default MNC value to use if device doesn't report MNC. */ - public static final ConfigKey<Integer> GEOLOCATION_MNC = new ConfigKey<>( + public static final ConfigKey<Integer> GEOLOCATION_MNC = new IntegerConfigKey( "geolocation.mnc", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Boolean flag to enable speed limit API to get speed limit values depending on location. Default value is false. */ - public static final ConfigKey<Boolean> SPEED_LIMIT_ENABLE = new ConfigKey<>( + public static final ConfigKey<Boolean> SPEED_LIMIT_ENABLE = new BooleanConfigKey( "speedLimit.enable", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Provider to use for speed limit. Available options: overpass. By default overpass is used. */ - public static final ConfigKey<String> SPEED_LIMIT_TYPE = new ConfigKey<>( + public static final ConfigKey<String> SPEED_LIMIT_TYPE = new StringConfigKey( "speedLimit.type", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Speed limit provider API URL address. */ - public static final ConfigKey<String> SPEED_LIMIT_URL = new ConfigKey<>( + public static final ConfigKey<String> SPEED_LIMIT_URL = new StringConfigKey( "speedLimit.url", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Override latitude sign / hemisphere. Useful in cases where value is incorrect because of device bug. Value can be * N for North or S for South. */ - public static final ConfigKey<String> LOCATION_LATITUDE_HEMISPHERE = new ConfigKey<>( + public static final ConfigKey<String> LOCATION_LATITUDE_HEMISPHERE = new StringConfigKey( "location.latitudeHemisphere", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Override longitude sign / hemisphere. Useful in cases where value is incorrect because of device bug. Value can * be E for East or W for West. */ - public static final ConfigKey<String> LOCATION_LONGITUDE_HEMISPHERE = new ConfigKey<>( + public static final ConfigKey<String> LOCATION_LONGITUDE_HEMISPHERE = new StringConfigKey( "location.longitudeHemisphere", - Collections.singletonList(KeyType.GLOBAL)); - - /** - * Enable Jetty Request Log. - */ - public static final ConfigKey<Boolean> WEB_REQUEST_LOG_ENABLE = new ConfigKey<>( - "web.requestLog.enable", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Jetty Request Log Path. @@ -1231,82 +1699,136 @@ public final class Keys { * over the file. * Example: ./logs/jetty-yyyy_mm_dd.request.log */ - public static final ConfigKey<String> WEB_REQUEST_LOG_PATH = new ConfigKey<>( + public static final ConfigKey<String> WEB_REQUEST_LOG_PATH = new StringConfigKey( "web.requestLog.path", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Set the number of days before rotated request log files are deleted. */ - public static final ConfigKey<Integer> WEB_REQUEST_LOG_RETAIN_DAYS = new ConfigKey<>( + public static final ConfigKey<Integer> WEB_REQUEST_LOG_RETAIN_DAYS = new IntegerConfigKey( "web.requestLog.retainDays", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Disable systemd health checks. */ - public static final ConfigKey<Boolean> WEB_DISABLE_HEALTH_CHECK = new ConfigKey<>( + public static final ConfigKey<Boolean> WEB_DISABLE_HEALTH_CHECK = new BooleanConfigKey( "web.disableHealthCheck", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Sets SameSite cookie attribute value. * Supported options: Lax, Strict, None. */ - public static final ConfigKey<String> WEB_SAME_SITE_COOKIE = new ConfigKey<>( + public static final ConfigKey<String> WEB_SAME_SITE_COOKIE = new StringConfigKey( "web.sameSiteCookie", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Enables persisting Jetty session to the database */ - public static final ConfigKey<Boolean> WEB_PERSIST_SESSION = new ConfigKey<>( + public static final ConfigKey<Boolean> WEB_PERSIST_SESSION = new BooleanConfigKey( "web.persistSession", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); + + /** + * Public URL for the web app. Used for notification, report link and OpenID Connect. + * If not provided, Traccar will attempt to get a URL from the server IP address, but it might be a local address. + */ + public static final ConfigKey<String> WEB_URL = new StringConfigKey( + "web.url", + List.of(KeyType.CONFIG)); /** * Output logging to the standard terminal output instead of a log file. */ - public static final ConfigKey<Boolean> LOGGER_CONSOLE = new ConfigKey<>( + public static final ConfigKey<Boolean> LOGGER_CONSOLE = new BooleanConfigKey( "logger.console", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); + + /** + * Log executed SQL queries. + */ + public static final ConfigKey<Boolean> LOGGER_QUERIES = new BooleanConfigKey( + "logger.queries", + List.of(KeyType.CONFIG)); /** * Log file name. For rotating logs, a date is added at the end of the file name for non-current logs. */ - public static final ConfigKey<String> LOGGER_FILE = new ConfigKey<>( + public static final ConfigKey<String> LOGGER_FILE = new StringConfigKey( "logger.file", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Logging level. Default value is 'info'. * Available options: off, severe, warning, info, config, fine, finer, finest, all. */ - public static final ConfigKey<String> LOGGER_LEVEL = new ConfigKey<>( + public static final ConfigKey<String> LOGGER_LEVEL = new StringConfigKey( "logger.level", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Print full exception traces. Useful for debugging. By default shortened traces are logged. */ - public static final ConfigKey<Boolean> LOGGER_FULL_STACK_TRACES = new ConfigKey<>( + public static final ConfigKey<Boolean> LOGGER_FULL_STACK_TRACES = new BooleanConfigKey( "logger.fullStackTraces", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); /** * Create a new log file daily. Helps with log management. For example, downloading and cleaning logs. Enabled by * default. */ - public static final ConfigKey<Boolean> LOGGER_ROTATE = new ConfigKey<>( + public static final ConfigKey<Boolean> LOGGER_ROTATE = new BooleanConfigKey( "logger.rotate", - Collections.singletonList(KeyType.GLOBAL)); + List.of(KeyType.CONFIG)); + + /** + * Log file rotation interval, the default rotation interval is once a day. + * This option is ignored if 'logger.rotate' = false + * Available options: day, hour + */ + public static final ConfigKey<String> LOGGER_ROTATE_INTERVAL = new StringConfigKey( + "logger.rotate.interval", + List.of(KeyType.CONFIG), + "day"); /** * A list of position attributes to log. */ - public static final ConfigKey<String> LOGGER_ATTRIBUTES = new ConfigKey<>( + public static final ConfigKey<String> LOGGER_ATTRIBUTES = new StringConfigKey( "logger.attributes", - Collections.singletonList(KeyType.GLOBAL), + List.of(KeyType.CONFIG), "time,position,speed,course,accuracy,result"); + /** + * Broadcast method. Available options are "multicast" and "redis". By default (if the value is not + * specified or does not matches available options) server disables broadcast. + */ + public static final ConfigKey<String> BROADCAST_TYPE = new StringConfigKey( + "broadcast.type", + List.of(KeyType.CONFIG)); + + /** + * Multicast interface. It can be either an IP address or an interface name. + */ + public static final ConfigKey<String> BROADCAST_INTERFACE = new StringConfigKey( + "broadcast.interface", + List.of(KeyType.CONFIG)); + + /** + * Multicast address or Redis URL for broadcasting synchronization events. + */ + public static final ConfigKey<String> BROADCAST_ADDRESS = new StringConfigKey( + "broadcast.address", + List.of(KeyType.CONFIG)); + + /** + * Multicast port for broadcasting synchronization events. + */ + public static final ConfigKey<Integer> BROADCAST_PORT = new IntegerConfigKey( + "broadcast.port", + List.of(KeyType.CONFIG)); + } diff --git a/src/main/java/org/traccar/database/AttributesManager.java b/src/main/java/org/traccar/database/AttributesManager.java deleted file mode 100644 index 28816645a..000000000 --- a/src/main/java/org/traccar/database/AttributesManager.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017 Anton Tananaev (anton@traccar.org) - * Copyright 2017 Andrey Kunitsyn (andrey@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.Attribute; - -public class AttributesManager extends ExtendedObjectManager<Attribute> { - - public AttributesManager(DataManager dataManager) { - super(dataManager, Attribute.class); - } - - @Override - public void updateCachedItem(Attribute attribute) { - Attribute cachedAttribute = getById(attribute.getId()); - cachedAttribute.setDescription(attribute.getDescription()); - cachedAttribute.setAttribute(attribute.getAttribute()); - cachedAttribute.setExpression(attribute.getExpression()); - cachedAttribute.setType(attribute.getType()); - } - -} diff --git a/src/main/java/org/traccar/database/BaseObjectManager.java b/src/main/java/org/traccar/database/BaseObjectManager.java deleted file mode 100644 index dd8b3bae4..000000000 --- a/src/main/java/org/traccar/database/BaseObjectManager.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org) - * Copyright 2017 Andrey Kunitsyn (andrey@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 java.util.Collection; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traccar.model.BaseModel; -import org.traccar.storage.StorageException; - -public class BaseObjectManager<T extends BaseModel> { - - private static final Logger LOGGER = LoggerFactory.getLogger(BaseObjectManager.class); - - private final ReadWriteLock lock = new ReentrantReadWriteLock(); - - private final DataManager dataManager; - - private final Class<T> baseClass; - private Map<Long, T> items; - - protected BaseObjectManager(DataManager dataManager, Class<T> baseClass) { - this.dataManager = dataManager; - this.baseClass = baseClass; - refreshItems(); - } - - protected final void readLock() { - lock.readLock().lock(); - } - - protected final void readUnlock() { - lock.readLock().unlock(); - } - - protected final void writeLock() { - lock.writeLock().lock(); - } - - protected final void writeUnlock() { - lock.writeLock().unlock(); - } - - protected final DataManager getDataManager() { - return dataManager; - } - - protected final Class<T> getBaseClass() { - return baseClass; - } - - public T getById(long itemId) { - try { - readLock(); - return items.get(itemId); - } finally { - readUnlock(); - } - } - - public void refreshItems() { - if (dataManager != null) { - try { - writeLock(); - Collection<T> databaseItems = dataManager.getObjects(baseClass); - if (items == null) { - items = new ConcurrentHashMap<>(databaseItems.size()); - } - Set<Long> databaseItemIds = new HashSet<>(); - for (T item : databaseItems) { - databaseItemIds.add(item.getId()); - if (items.containsKey(item.getId())) { - updateCachedItem(item); - } else { - addNewItem(item); - } - } - for (Long cachedItemId : items.keySet()) { - if (!databaseItemIds.contains(cachedItemId)) { - removeCachedItem(cachedItemId); - } - } - } catch (StorageException error) { - LOGGER.warn("Error refreshing items", error); - } finally { - writeUnlock(); - } - } - } - - protected void addNewItem(T item) { - try { - writeLock(); - items.put(item.getId(), item); - } finally { - writeUnlock(); - } - } - - public void addItem(T item) throws StorageException { - dataManager.addObject(item); - addNewItem(item); - } - - protected void updateCachedItem(T item) { - try { - writeLock(); - items.put(item.getId(), item); - } finally { - writeUnlock(); - } - } - - public void updateItem(T item) throws StorageException { - dataManager.updateObject(item); - updateCachedItem(item); - } - - protected void removeCachedItem(long itemId) { - try { - writeLock(); - items.remove(itemId); - } finally { - writeUnlock(); - } - } - - public void removeItem(long itemId) throws StorageException { - BaseModel item = getById(itemId); - if (item != null) { - dataManager.removeObject(baseClass, itemId); - removeCachedItem(itemId); - } - } - - public final Collection<T> getItems(Set<Long> itemIds) { - Collection<T> result = new LinkedList<>(); - for (long itemId : itemIds) { - T item = getById(itemId); - if (item != null) { - result.add(item); - } - } - return result; - } - - public Set<Long> getAllItems() { - try { - readLock(); - return items.keySet(); - } finally { - readUnlock(); - } - } - -} diff --git a/src/main/java/org/traccar/database/CommandsManager.java b/src/main/java/org/traccar/database/CommandsManager.java index 843c89e82..fb8f2f9d6 100644 --- a/src/main/java/org/traccar/database/CommandsManager.java +++ b/src/main/java/org/traccar/database/CommandsManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org) * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,177 +16,112 @@ */ package org.traccar.database; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Queue; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.traccar.BaseProtocol; -import org.traccar.Context; +import org.traccar.ServerManager; +import org.traccar.broadcast.BroadcastInterface; +import org.traccar.broadcast.BroadcastService; import org.traccar.model.Command; -import org.traccar.model.Typed; +import org.traccar.model.Device; import org.traccar.model.Position; - -public class CommandsManager extends ExtendedObjectManager<Command> { - - private static final Logger LOGGER = LoggerFactory.getLogger(CommandsManager.class); - - private final Map<Long, Queue<Command>> deviceQueues = new ConcurrentHashMap<>(); - - private final boolean queueing; - - public CommandsManager(DataManager dataManager, boolean queueing) { - super(dataManager, Command.class); - this.queueing = queueing; - } - - public boolean checkDeviceCommand(long deviceId, long commandId) { - return !getAllDeviceItems(deviceId).contains(commandId); +import org.traccar.model.QueuedCommand; +import org.traccar.session.ConnectionManager; +import org.traccar.session.DeviceSession; +import org.traccar.sms.SmsManager; +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.Order; +import org.traccar.storage.query.Request; + +import jakarta.annotation.Nullable; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import java.util.Collection; +import java.util.stream.Collectors; + +@Singleton +public class CommandsManager implements BroadcastInterface { + + private final Storage storage; + private final ServerManager serverManager; + private final SmsManager smsManager; + private final ConnectionManager connectionManager; + private final BroadcastService broadcastService; + + @Inject + public CommandsManager( + Storage storage, ServerManager serverManager, @Nullable SmsManager smsManager, + ConnectionManager connectionManager, BroadcastService broadcastService) { + this.storage = storage; + this.serverManager = serverManager; + this.smsManager = smsManager; + this.connectionManager = connectionManager; + this.broadcastService = broadcastService; + broadcastService.registerListener(this); } public boolean sendCommand(Command command) throws Exception { long deviceId = command.getDeviceId(); - if (command.getId() != 0) { - command = getById(command.getId()).clone(); - command.setDeviceId(deviceId); - } if (command.getTextChannel()) { - Position lastPosition = Context.getIdentityManager().getLastPosition(deviceId); - String phone = Context.getIdentityManager().getById(deviceId).getPhone(); - if (lastPosition != null) { - BaseProtocol protocol = Context.getServerManager().getProtocol(lastPosition.getProtocol()); - protocol.sendTextCommand(phone, command); + if (smsManager == null) { + throw new RuntimeException("SMS not configured"); + } + Device device = storage.getObject(Device.class, new Request( + new Columns.Include("positionId", "phone"), new Condition.Equals("id", deviceId))); + Position position = storage.getObject(Position.class, new Request( + new Columns.All(), new Condition.Equals("id", device.getPositionId()))); + if (position != null) { + BaseProtocol protocol = serverManager.getProtocol(position.getProtocol()); + protocol.sendTextCommand(device.getPhone(), command); } else if (command.getType().equals(Command.TYPE_CUSTOM)) { - if (Context.getSmsManager() != null) { - Context.getSmsManager().sendMessageSync(phone, command.getString(Command.KEY_DATA), true); - } else { - throw new RuntimeException("SMS is not enabled"); - } + smsManager.sendMessage(device.getPhone(), command.getString(Command.KEY_DATA), true); } else { throw new RuntimeException("Command " + command.getType() + " is not supported"); } } else { - ActiveDevice activeDevice = Context.getConnectionManager().getActiveDevice(deviceId); - if (activeDevice != null) { - if (activeDevice.supportsLiveCommands()) { - activeDevice.sendCommand(command); - } else { - getDeviceQueue(deviceId).add(command); - return false; - } - } else if (!queueing) { - throw new RuntimeException("Device is not online"); + DeviceSession deviceSession = connectionManager.getDeviceSession(deviceId); + if (deviceSession != null && deviceSession.supportsLiveCommands()) { + deviceSession.sendCommand(command); } else { - getDeviceQueue(deviceId).add(command); + storage.addObject(QueuedCommand.fromCommand(command), new Request(new Columns.Exclude("id"))); + broadcastService.updateCommand(true, deviceId); return false; } } return true; } - public Collection<Long> getSupportedCommands(long deviceId) { - List<Long> result = new ArrayList<>(); - Position lastPosition = Context.getIdentityManager().getLastPosition(deviceId); - for (long commandId : getAllDeviceItems(deviceId)) { - Command command = getById(commandId); - if (lastPosition != null) { - BaseProtocol protocol = Context.getServerManager().getProtocol(lastPosition.getProtocol()); - if (command.getTextChannel() && protocol.getSupportedTextCommands().contains(command.getType()) - || !command.getTextChannel() - && protocol.getSupportedDataCommands().contains(command.getType())) { - result.add(commandId); - } - } else if (command.getType().equals(Command.TYPE_CUSTOM)) { - result.add(commandId); - } - } - return result; - } - - public Collection<Typed> getCommandTypes(long deviceId, boolean textChannel) { - Position lastPosition = Context.getIdentityManager().getLastPosition(deviceId); - if (lastPosition != null) { - return getCommandTypes(lastPosition.getProtocol(), textChannel); - } else { - return Collections.singletonList(new Typed(Command.TYPE_CUSTOM)); - } - } - - public Collection<Typed> getCommandTypes(String protocolName, boolean textChannel) { - List<Typed> result = new ArrayList<>(); - BaseProtocol protocol = Context.getServerManager().getProtocol(protocolName); - Collection<String> commands; - commands = textChannel ? protocol.getSupportedTextCommands() : protocol.getSupportedDataCommands(); - for (String commandKey : commands) { - result.add(new Typed(commandKey)); - } - return result; - } - - public Collection<Typed> getAllCommandTypes() { - List<Typed> result = new ArrayList<>(); - Field[] fields = Command.class.getDeclaredFields(); - for (Field field : fields) { - if (Modifier.isStatic(field.getModifiers()) && field.getName().startsWith("TYPE_")) { - try { - result.add(new Typed(field.get(null).toString())); - } catch (IllegalArgumentException | IllegalAccessException error) { - LOGGER.warn("Get command types error", error); - } - } - } - return result; - } - - private Queue<Command> getDeviceQueue(long deviceId) { - Queue<Command> deviceQueue; - try { - readLock(); - deviceQueue = deviceQueues.get(deviceId); - } finally { - readUnlock(); - } - if (deviceQueue != null) { - return deviceQueue; - } else { - try { - writeLock(); - return deviceQueues.computeIfAbsent(deviceId, key -> new ConcurrentLinkedQueue<>()); - } finally { - writeUnlock(); - } - } - } - public Collection<Command> readQueuedCommands(long deviceId) { return readQueuedCommands(deviceId, Integer.MAX_VALUE); } public Collection<Command> readQueuedCommands(long deviceId, int count) { - Queue<Command> deviceQueue; try { - readLock(); - deviceQueue = deviceQueues.get(deviceId); - } finally { - readUnlock(); + var commands = storage.getObjects(QueuedCommand.class, new Request( + new Columns.All(), + new Condition.Equals("deviceId", deviceId), + new Order("id", false, count))); + for (var command : commands) { + storage.removeObject(QueuedCommand.class, new Request( + new Condition.Equals("id", command.getId()))); + } + return commands.stream().map(QueuedCommand::toCommand).collect(Collectors.toList()); + } catch (StorageException e) { + throw new RuntimeException(e); } - Collection<Command> result = new ArrayList<>(); - if (deviceQueue != null) { - Command command = deviceQueue.poll(); - while (command != null && result.size() < count) { - result.add(command); - command = deviceQueue.poll(); + } + + @Override + public void updateCommand(boolean local, long deviceId) { + if (!local) { + DeviceSession deviceSession = connectionManager.getDeviceSession(deviceId); + if (deviceSession != null && deviceSession.supportsLiveCommands()) { + for (Command command : readQueuedCommands(deviceId)) { + deviceSession.sendCommand(command); + } } } - return result; } } diff --git a/src/main/java/org/traccar/database/ConnectionManager.java b/src/main/java/org/traccar/database/ConnectionManager.java deleted file mode 100644 index 359061f00..000000000 --- a/src/main/java/org/traccar/database/ConnectionManager.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright 2015 - 2020 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.Channel; -import io.netty.util.Timeout; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traccar.Context; -import org.traccar.GlobalTimer; -import org.traccar.Main; -import org.traccar.Protocol; -import org.traccar.config.Keys; -import org.traccar.handler.events.MotionEventHandler; -import org.traccar.handler.events.OverspeedEventHandler; -import org.traccar.model.Device; -import org.traccar.model.DeviceState; -import org.traccar.model.Event; -import org.traccar.model.Position; -import org.traccar.storage.StorageException; - -import java.net.SocketAddress; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; - -public class ConnectionManager { - - private static final Logger LOGGER = LoggerFactory.getLogger(ConnectionManager.class); - - private final long deviceTimeout; - private final boolean updateDeviceState; - - private final Map<Long, ActiveDevice> activeDevices = new ConcurrentHashMap<>(); - private final Map<Long, Set<UpdateListener>> listeners = new ConcurrentHashMap<>(); - private final Map<Long, Timeout> timeouts = new ConcurrentHashMap<>(); - - public ConnectionManager() { - deviceTimeout = Context.getConfig().getLong(Keys.STATUS_TIMEOUT) * 1000; - updateDeviceState = Context.getConfig().getBoolean(Keys.STATUS_UPDATE_DEVICE_STATE); - } - - public void addActiveDevice(long deviceId, Protocol protocol, Channel channel, SocketAddress remoteAddress) { - activeDevices.put(deviceId, new ActiveDevice(deviceId, protocol, channel, remoteAddress)); - } - - public void removeActiveDevice(Channel channel) { - for (ActiveDevice activeDevice : activeDevices.values()) { - if (activeDevice.getChannel() == channel) { - updateDevice(activeDevice.getDeviceId(), Device.STATUS_OFFLINE, null); - activeDevices.remove(activeDevice.getDeviceId()); - break; - } - } - } - - public ActiveDevice getActiveDevice(long deviceId) { - return activeDevices.get(deviceId); - } - - public void updateDevice(final long deviceId, String status, Date time) { - Device device = Context.getIdentityManager().getById(deviceId); - if (device == null) { - return; - } - - String oldStatus = device.getStatus(); - device.setStatus(status); - - if (!status.equals(oldStatus)) { - String eventType; - Map<Event, Position> events = new HashMap<>(); - switch (status) { - case Device.STATUS_ONLINE: - eventType = Event.TYPE_DEVICE_ONLINE; - break; - case Device.STATUS_UNKNOWN: - eventType = Event.TYPE_DEVICE_UNKNOWN; - if (updateDeviceState) { - events.putAll(updateDeviceState(deviceId)); - } - break; - default: - eventType = Event.TYPE_DEVICE_OFFLINE; - if (updateDeviceState) { - events.putAll(updateDeviceState(deviceId)); - } - break; - } - events.put(new Event(eventType, deviceId), null); - Context.getNotificationManager().updateEvents(events); - } - - Timeout timeout = timeouts.remove(deviceId); - if (timeout != null) { - timeout.cancel(); - } - - if (time != null) { - device.setLastUpdate(time); - } - - if (status.equals(Device.STATUS_ONLINE)) { - timeouts.put(deviceId, GlobalTimer.getTimer().newTimeout(timeout1 -> { - if (!timeout1.isCancelled()) { - updateDevice(deviceId, Device.STATUS_UNKNOWN, null); - } - }, deviceTimeout, TimeUnit.MILLISECONDS)); - } - - try { - Context.getDeviceManager().updateDeviceStatus(device); - } catch (StorageException e) { - LOGGER.warn("Update device status error", e); - } - - updateDevice(device); - } - - public Map<Event, Position> updateDeviceState(long deviceId) { - DeviceState deviceState = Context.getDeviceManager().getDeviceState(deviceId); - Map<Event, Position> result = new HashMap<>(); - - Map<Event, Position> event = Main.getInjector() - .getInstance(MotionEventHandler.class).updateMotionState(deviceState); - if (event != null) { - result.putAll(event); - } - - event = Main.getInjector().getInstance(OverspeedEventHandler.class) - .updateOverspeedState(deviceState, Context.getDeviceManager(). - lookupAttributeDouble(deviceId, OverspeedEventHandler.ATTRIBUTE_SPEED_LIMIT, 0, true, false)); - if (event != null) { - result.putAll(event); - } - - return result; - } - - public synchronized void sendKeepalive() { - for (Set<UpdateListener> userListeners : listeners.values()) { - for (UpdateListener listener : userListeners) { - listener.onKeepalive(); - } - } - } - - public synchronized void updateDevice(Device device) { - for (long userId : Context.getPermissionsManager().getDeviceUsers(device.getId())) { - if (listeners.containsKey(userId)) { - for (UpdateListener listener : listeners.get(userId)) { - listener.onUpdateDevice(device); - } - } - } - } - - public synchronized void updatePosition(Position position) { - long deviceId = position.getDeviceId(); - - for (long userId : Context.getPermissionsManager().getDeviceUsers(deviceId)) { - if (listeners.containsKey(userId)) { - for (UpdateListener listener : listeners.get(userId)) { - listener.onUpdatePosition(position); - } - } - } - } - - public synchronized void updateEvent(long userId, Event event) { - if (listeners.containsKey(userId)) { - for (UpdateListener listener : listeners.get(userId)) { - listener.onUpdateEvent(event); - } - } - } - - public interface UpdateListener { - void onKeepalive(); - void onUpdateDevice(Device device); - void onUpdatePosition(Position position); - void onUpdateEvent(Event event); - } - - public synchronized void addListener(long userId, UpdateListener listener) { - if (!listeners.containsKey(userId)) { - listeners.put(userId, new HashSet<>()); - } - listeners.get(userId).add(listener); - } - - public synchronized void removeListener(long userId, UpdateListener listener) { - if (!listeners.containsKey(userId)) { - listeners.put(userId, new HashSet<>()); - } - listeners.get(userId).remove(listener); - } - -} diff --git a/src/main/java/org/traccar/database/DataManager.java b/src/main/java/org/traccar/database/DataManager.java deleted file mode 100644 index 9ac808a69..000000000 --- a/src/main/java/org/traccar/database/DataManager.java +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * 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 com.zaxxer.hikari.HikariConfig; -import com.zaxxer.hikari.HikariDataSource; -import liquibase.Contexts; -import liquibase.Liquibase; -import liquibase.database.Database; -import liquibase.database.DatabaseFactory; -import liquibase.exception.LiquibaseException; -import liquibase.resource.FileSystemResourceAccessor; -import liquibase.resource.ResourceAccessor; -import org.traccar.Context; -import org.traccar.config.Config; -import org.traccar.config.Keys; -import org.traccar.model.BaseModel; -import org.traccar.model.Device; -import org.traccar.model.Event; -import org.traccar.model.Permission; -import org.traccar.model.Position; -import org.traccar.model.Server; -import org.traccar.model.Statistics; -import org.traccar.model.User; -import org.traccar.storage.DatabaseStorage; -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.Limit; -import org.traccar.storage.query.Order; -import org.traccar.storage.query.Request; - -import javax.sql.DataSource; -import java.io.File; -import java.lang.reflect.Method; -import java.net.URL; -import java.util.Collection; -import java.util.Date; -import java.util.LinkedList; -import java.util.List; - -public class DataManager { - - private final Config config; - - private DataSource dataSource; - - public DataSource getDataSource() { - return dataSource; - } - - private final Storage storage; - - public Storage getStorage() { - return storage; - } - - private final boolean forceLdap; - - public DataManager(Config config) throws Exception { - this.config = config; - - forceLdap = config.getBoolean(Keys.LDAP_FORCE); - - initDatabase(); - initDatabaseSchema(); - - storage = new DatabaseStorage(dataSource); - } - - private void initDatabase() throws Exception { - - String driverFile = config.getString(Keys.DATABASE_DRIVER_FILE); - if (driverFile != null) { - ClassLoader classLoader = ClassLoader.getSystemClassLoader(); - try { - Method method = classLoader.getClass().getDeclaredMethod("addURL", URL.class); - method.setAccessible(true); - method.invoke(classLoader, new File(driverFile).toURI().toURL()); - } catch (NoSuchMethodException e) { - Method method = classLoader.getClass() - .getDeclaredMethod("appendToClassPathForInstrumentation", String.class); - method.setAccessible(true); - method.invoke(classLoader, driverFile); - } - } - - String driver = config.getString(Keys.DATABASE_DRIVER); - if (driver != null) { - Class.forName(driver); - } - - HikariConfig hikariConfig = new HikariConfig(); - hikariConfig.setDriverClassName(driver); - hikariConfig.setJdbcUrl(config.getString(Keys.DATABASE_URL)); - hikariConfig.setUsername(config.getString(Keys.DATABASE_USER)); - hikariConfig.setPassword(config.getString(Keys.DATABASE_PASSWORD)); - hikariConfig.setConnectionInitSql(config.getString(Keys.DATABASE_CHECK_CONNECTION)); - hikariConfig.setIdleTimeout(600000); - - int maxPoolSize = config.getInteger(Keys.DATABASE_MAX_POOL_SIZE); - if (maxPoolSize != 0) { - hikariConfig.setMaximumPoolSize(maxPoolSize); - } - - dataSource = new HikariDataSource(hikariConfig); - } - - private void initDatabaseSchema() throws LiquibaseException { - - if (config.hasKey(Keys.DATABASE_CHANGELOG)) { - - ResourceAccessor resourceAccessor = new FileSystemResourceAccessor(new File(".")); - - Database database = DatabaseFactory.getInstance().openDatabase( - config.getString(Keys.DATABASE_URL), - config.getString(Keys.DATABASE_USER), - config.getString(Keys.DATABASE_PASSWORD), - config.getString(Keys.DATABASE_DRIVER), - null, null, null, resourceAccessor); - - Liquibase liquibase = new Liquibase( - config.getString(Keys.DATABASE_CHANGELOG), resourceAccessor, database); - - liquibase.clearCheckSums(); - - liquibase.update(new Contexts()); - } - } - - public User login(String email, String password) throws StorageException { - User user = storage.getObject(User.class, new Request( - new Columns.Include("id", "login", "hashedPassword", "salt"), - new Condition.Or( - new Condition.Equals("email", "email", email.trim()), - new Condition.Equals("login", "email")))); - LdapProvider ldapProvider = Context.getLdapProvider(); - if (user != null) { - if (ldapProvider != null && user.getLogin() != null && ldapProvider.login(user.getLogin(), password) - || !forceLdap && user.isPasswordValid(password)) { - return user; - } - } else { - if (ldapProvider != null && ldapProvider.login(email, password)) { - user = ldapProvider.getUser(email); - Context.getUsersManager().addItem(user); - return user; - } - } - return null; - } - - public void updateUserPassword(User user) throws StorageException { - storage.updateObject(user, new Request( - new Columns.Include("hashedPassword", "salt"), - new Condition.Equals("id", "id"))); - } - - public void updateDeviceStatus(Device device) throws StorageException { - storage.updateObject(device, new Request( - new Columns.Include("lastUpdate"), - new Condition.Equals("id", "id"))); - } - - public Collection<Position> getPositions(long deviceId, Date from, Date to) throws StorageException { - return storage.getObjects(Position.class, new Request( - new Columns.All(), - new Condition.And( - new Condition.Equals("deviceId", "deviceId", deviceId), - new Condition.Between("fixTime", "from", from, "to", to)), - new Order("fixTime"))); - } - - public Position getPrecedingPosition(long deviceId, Date date) throws StorageException { - return storage.getObject(Position.class, new Request( - new Columns.All(), - new Condition.And( - new Condition.Equals("deviceId", "deviceId", deviceId), - new Condition.Compare("fixTime", "<=", "time", date)), - new Order(true, "fixTime"), - new Limit(1))); - } - - public void updateLatestPosition(Position position) throws StorageException { - Device device = new Device(); - device.setId(position.getDeviceId()); - device.setPositionId(position.getId()); - storage.updateObject(device, new Request( - new Columns.Include("positionId"), - new Condition.Equals("id", "id"))); - } - - public Collection<Position> getLatestPositions() throws StorageException { - List<Position> positions = new LinkedList<>(); - List<Device> devices = storage.getObjects(Device.class, new Request(new Columns.Include("positionId"))); - for (Device device : devices) { - positions.addAll(storage.getObjects(Position.class, new Request( - new Columns.All(), - new Condition.Equals("id", "id", device.getPositionId())))); - } - return positions; - } - - public Server getServer() throws StorageException { - return storage.getObject(Server.class, new Request(new Columns.All())); - } - - public Collection<Event> getEvents(long deviceId, Date from, Date to) throws StorageException { - return storage.getObjects(Event.class, new Request( - new Columns.All(), - new Condition.And( - new Condition.Equals("deviceId", "deviceId", deviceId), - new Condition.Between("eventTime", "from", from, "to", to)), - new Order("eventTime"))); - } - - public Collection<Statistics> getStatistics(Date from, Date to) throws StorageException { - return storage.getObjects(Statistics.class, new Request( - new Columns.All(), - new Condition.Between("captureTime", "from", from, "to", to), - new Order("captureTime"))); - } - - public Collection<Permission> getPermissions(Class<? extends BaseModel> owner, Class<? extends BaseModel> property) - throws StorageException, ClassNotFoundException { - return storage.getPermissions(owner, property); - } - - public void linkObject(Class<?> owner, long ownerId, Class<?> property, long propertyId, boolean link) - throws StorageException { - if (link) { - storage.addPermission(new Permission(owner, ownerId, property, propertyId)); - } else { - storage.removePermission(new Permission(owner, ownerId, property, propertyId)); - } - } - - public <T extends BaseModel> T getObject(Class<T> clazz, long entityId) throws StorageException { - return storage.getObject(clazz, new Request( - new Columns.All(), - new Condition.Equals("id", "id", entityId))); - } - - public <T extends BaseModel> Collection<T> getObjects(Class<T> clazz) throws StorageException { - return storage.getObjects(clazz, new Request(new Columns.All())); - } - - public void addObject(BaseModel entity) throws StorageException { - entity.setId(storage.addObject(entity, new Request(new Columns.Exclude("id")))); - } - - public void updateObject(BaseModel entity) throws StorageException { - storage.updateObject(entity, new Request( - new Columns.Exclude("id"), - new Condition.Equals("id", "id"))); - } - - public void removeObject(Class<? extends BaseModel> clazz, long entityId) throws StorageException { - storage.removeObject(clazz, new Request(new Condition.Equals("id", "id", entityId))); - } - -} diff --git a/src/main/java/org/traccar/database/DeviceLookupService.java b/src/main/java/org/traccar/database/DeviceLookupService.java new file mode 100644 index 000000000..6ec6841a1 --- /dev/null +++ b/src/main/java/org/traccar/database/DeviceLookupService.java @@ -0,0 +1,141 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.database; + +import io.netty.util.Timeout; +import io.netty.util.Timer; +import io.netty.util.TimerTask; +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.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.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +@Singleton +public class DeviceLookupService { + + private static final Logger LOGGER = LoggerFactory.getLogger(DeviceLookupService.class); + + private static final long INFO_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(60); + private static final long THROTTLE_MIN_MS = TimeUnit.MINUTES.toMillis(1); + private static final long THROTTLE_MAX_MS = TimeUnit.MINUTES.toMillis(30); + + private final Storage storage; + private final Timer timer; + + private final boolean throttlingEnabled; + + private static class IdentifierInfo { + private long lastQuery; + private long delay; + private Timeout timeout; + } + + private final class IdentifierTask implements TimerTask { + private final String uniqueId; + + private IdentifierTask(String uniqueId) { + this.uniqueId = uniqueId; + } + + @Override + public void run(Timeout timeout) { + LOGGER.debug("Device lookup expired {}", uniqueId); + synchronized (DeviceLookupService.this) { + identifierMap.remove(uniqueId); + } + } + } + + private final Map<String, IdentifierInfo> identifierMap = new ConcurrentHashMap<>(); + + @Inject + public DeviceLookupService(Config config, Storage storage, Timer timer) { + this.storage = storage; + this.timer = timer; + throttlingEnabled = config.getBoolean(Keys.DATABASE_THROTTLE_UNKNOWN); + } + + private synchronized boolean isThrottled(String uniqueId) { + if (throttlingEnabled) { + IdentifierInfo info = identifierMap.get(uniqueId); + return info != null && System.currentTimeMillis() < info.lastQuery + info.delay; + } else { + return false; + } + } + + private synchronized void lookupSucceeded(String uniqueId) { + if (throttlingEnabled) { + IdentifierInfo info = identifierMap.remove(uniqueId); + if (info != null) { + info.timeout.cancel(); + } + } + } + + private synchronized void lookupFailed(String uniqueId) { + if (throttlingEnabled) { + IdentifierInfo info = identifierMap.get(uniqueId); + if (info != null) { + info.timeout.cancel(); + info.delay = Math.min(info.delay * 2, THROTTLE_MAX_MS); + } else { + info = new IdentifierInfo(); + identifierMap.put(uniqueId, info); + info.delay = THROTTLE_MIN_MS; + } + info.lastQuery = System.currentTimeMillis(); + info.timeout = timer.newTimeout(new IdentifierTask(uniqueId), INFO_TIMEOUT_MS, TimeUnit.MILLISECONDS); + LOGGER.debug("Device lookup {} throttled for {} ms", uniqueId, info.delay); + } + } + + public Device lookup(String[] uniqueIds) { + Device device = null; + try { + for (String uniqueId : uniqueIds) { + if (!isThrottled(uniqueId)) { + device = storage.getObject(Device.class, new Request( + new Columns.All(), new Condition.Equals("uniqueId", uniqueId))); + if (device != null) { + lookupSucceeded(uniqueId); + break; + } else { + lookupFailed(uniqueId); + } + } else { + LOGGER.debug("Device lookup throttled {}", uniqueId); + } + } + } catch (StorageException e) { + LOGGER.warn("Find device error", e); + } + return device; + } + +} diff --git a/src/main/java/org/traccar/database/DeviceManager.java b/src/main/java/org/traccar/database/DeviceManager.java deleted file mode 100644 index 40591e869..000000000 --- a/src/main/java/org/traccar/database/DeviceManager.java +++ /dev/null @@ -1,471 +0,0 @@ -/* - * Copyright 2016 - 2020 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 java.sql.SQLException; -import java.util.Collection; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicLong; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traccar.config.Config; -import org.traccar.Context; -import org.traccar.config.Keys; -import org.traccar.model.Command; -import org.traccar.model.Device; -import org.traccar.model.DeviceState; -import org.traccar.model.DeviceAccumulators; -import org.traccar.model.Group; -import org.traccar.model.Position; -import org.traccar.model.Server; -import org.traccar.storage.StorageException; - -public class DeviceManager extends BaseObjectManager<Device> implements IdentityManager, ManagableObjects { - - private static final Logger LOGGER = LoggerFactory.getLogger(DeviceManager.class); - - private final Config config; - private final long dataRefreshDelay; - - private Map<String, Device> devicesByUniqueId; - private Map<String, Device> devicesByPhone; - private final AtomicLong devicesLastUpdate = new AtomicLong(); - - private final Map<Long, Position> positions = new ConcurrentHashMap<>(); - - private final Map<Long, DeviceState> deviceStates = new ConcurrentHashMap<>(); - - public DeviceManager(DataManager dataManager) { - super(dataManager, Device.class); - this.config = Context.getConfig(); - try { - writeLock(); - if (devicesByPhone == null) { - devicesByPhone = new ConcurrentHashMap<>(); - } - if (devicesByUniqueId == null) { - devicesByUniqueId = new ConcurrentHashMap<>(); - } - } finally { - writeUnlock(); - } - dataRefreshDelay = config.getLong(Keys.DATABASE_REFRESH_DELAY) * 1000; - refreshLastPositions(); - } - - @Override - public long addUnknownDevice(String uniqueId) { - Device device = new Device(); - device.setName(uniqueId); - device.setUniqueId(uniqueId); - device.setCategory(Context.getConfig().getString(Keys.DATABASE_REGISTER_UNKNOWN_DEFAULT_CATEGORY)); - - long defaultGroupId = Context.getConfig().getLong(Keys.DATABASE_REGISTER_UNKNOWN_DEFAULT_GROUP_ID); - if (defaultGroupId != 0) { - device.setGroupId(defaultGroupId); - } - - try { - addItem(device); - - LOGGER.info("Automatically registered device " + uniqueId); - - if (defaultGroupId != 0) { - Context.getPermissionsManager().refreshDeviceAndGroupPermissions(); - Context.getPermissionsManager().refreshAllExtendedPermissions(); - } - - return device.getId(); - } catch (StorageException e) { - LOGGER.warn("Automatic device registration error", e); - return 0; - } - } - - public void updateDeviceCache(boolean force) { - long lastUpdate = devicesLastUpdate.get(); - if ((force || System.currentTimeMillis() - lastUpdate > dataRefreshDelay) - && devicesLastUpdate.compareAndSet(lastUpdate, System.currentTimeMillis())) { - refreshItems(); - } - } - - @Override - public Device getByUniqueId(String uniqueId) throws SQLException { - boolean forceUpdate; - try { - readLock(); - forceUpdate = !devicesByUniqueId.containsKey(uniqueId) && !config.getBoolean(Keys.DATABASE_IGNORE_UNKNOWN); - } finally { - readUnlock(); - } - updateDeviceCache(forceUpdate); - try { - readLock(); - return devicesByUniqueId.get(uniqueId); - } finally { - readUnlock(); - } - } - - @Override - public String getDevicePassword(long id, String protocol, String defaultPassword) { - - String password = lookupAttributeString(id, Command.KEY_DEVICE_PASSWORD, null, false, false); - if (password != null) { - return password; - } - - if (protocol != null) { - password = Context.getConfig().getString(Keys.PROTOCOL_DEVICE_PASSWORD.withPrefix(protocol)); - if (password != null) { - return password; - } - } - - return defaultPassword; - } - - @Override - public Set<Long> getAllItems() { - Set<Long> result = super.getAllItems(); - if (result.isEmpty()) { - updateDeviceCache(true); - result = super.getAllItems(); - } - return result; - } - - public Collection<Device> getAllDevices() { - return getItems(getAllItems()); - } - - public Set<Long> getAllUserItems(long userId) { - return Context.getPermissionsManager().getDevicePermissions(userId); - } - - @Override - public Set<Long> getUserItems(long userId) { - if (Context.getPermissionsManager() != null) { - Set<Long> result = new HashSet<>(); - for (long deviceId : Context.getPermissionsManager().getDevicePermissions(userId)) { - Device device = getById(deviceId); - if (device != null && !device.getDisabled()) { - result.add(deviceId); - } - } - return result; - } else { - return new HashSet<>(); - } - } - - public Set<Long> getAllManagedItems(long userId) { - Set<Long> result = new HashSet<>(getAllUserItems(userId)); - for (long managedUserId : Context.getUsersManager().getUserItems(userId)) { - result.addAll(getAllUserItems(managedUserId)); - } - return result; - } - - @Override - public Set<Long> getManagedItems(long userId) { - Set<Long> result = new HashSet<>(getUserItems(userId)); - for (long managedUserId : Context.getUsersManager().getUserItems(userId)) { - result.addAll(getUserItems(managedUserId)); - } - return result; - } - - private void addByUniqueId(Device device) { - try { - writeLock(); - if (devicesByUniqueId == null) { - devicesByUniqueId = new ConcurrentHashMap<>(); - } - devicesByUniqueId.put(device.getUniqueId(), device); - } finally { - writeUnlock(); - } - } - - private void removeByUniqueId(String deviceUniqueId) { - try { - writeLock(); - if (devicesByUniqueId != null) { - devicesByUniqueId.remove(deviceUniqueId); - } - } finally { - writeUnlock(); - } - } - - private void addByPhone(Device device) { - try { - writeLock(); - if (devicesByPhone == null) { - devicesByPhone = new ConcurrentHashMap<>(); - } - devicesByPhone.put(device.getPhone(), device); - } finally { - writeUnlock(); - } - } - - private void removeByPhone(String phone) { - if (phone == null || phone.isEmpty()) { - return; - } - try { - writeLock(); - if (devicesByPhone != null) { - devicesByPhone.remove(phone); - } - } finally { - writeUnlock(); - } - } - - @Override - protected void addNewItem(Device device) { - super.addNewItem(device); - addByUniqueId(device); - if (device.getPhone() != null && !device.getPhone().isEmpty()) { - addByPhone(device); - } - if (Context.getGeofenceManager() != null) { - Position lastPosition = getLastPosition(device.getId()); - if (lastPosition != null) { - device.setGeofenceIds(Context.getGeofenceManager().getCurrentDeviceGeofences(lastPosition)); - } - } - } - - @Override - protected void updateCachedItem(Device device) { - Device cachedDevice = getById(device.getId()); - cachedDevice.setName(device.getName()); - cachedDevice.setGroupId(device.getGroupId()); - cachedDevice.setCategory(device.getCategory()); - cachedDevice.setContact(device.getContact()); - cachedDevice.setModel(device.getModel()); - cachedDevice.setDisabled(device.getDisabled()); - cachedDevice.setAttributes(device.getAttributes()); - if (!device.getUniqueId().equals(cachedDevice.getUniqueId())) { - removeByUniqueId(cachedDevice.getUniqueId()); - cachedDevice.setUniqueId(device.getUniqueId()); - addByUniqueId(cachedDevice); - } - if (device.getPhone() != null && !device.getPhone().isEmpty() - && !device.getPhone().equals(cachedDevice.getPhone())) { - String phone = cachedDevice.getPhone(); - removeByPhone(phone); - cachedDevice.setPhone(device.getPhone()); - addByPhone(cachedDevice); - } - } - - @Override - protected void removeCachedItem(long deviceId) { - Device cachedDevice = getById(deviceId); - if (cachedDevice != null) { - String deviceUniqueId = cachedDevice.getUniqueId(); - String phone = cachedDevice.getPhone(); - super.removeCachedItem(deviceId); - removeByUniqueId(deviceUniqueId); - removeByPhone(phone); - } - positions.remove(deviceId); - } - - public void updateDeviceStatus(Device device) throws StorageException { - getDataManager().updateDeviceStatus(device); - Device cachedDevice = getById(device.getId()); - if (cachedDevice != null) { - cachedDevice.setStatus(device.getStatus()); - } - } - - private void refreshLastPositions() { - if (getDataManager() != null) { - try { - for (Position position : getDataManager().getLatestPositions()) { - positions.put(position.getDeviceId(), position); - } - } catch (StorageException error) { - LOGGER.warn("Load latest positions error", error); - } - } - } - - public boolean isLatestPosition(Position position) { - Position lastPosition = getLastPosition(position.getDeviceId()); - return lastPosition == null || position.getFixTime().compareTo(lastPosition.getFixTime()) >= 0; - } - - public void updateLatestPosition(Position position) throws StorageException { - - if (isLatestPosition(position)) { - - getDataManager().updateLatestPosition(position); - - Device device = getById(position.getDeviceId()); - if (device != null) { - device.setPositionId(position.getId()); - } - - positions.put(position.getDeviceId(), position); - - if (Context.getConnectionManager() != null) { - Context.getConnectionManager().updatePosition(position); - } - } - } - - @Override - public Position getLastPosition(long deviceId) { - return positions.get(deviceId); - } - - public Collection<Position> getInitialState(long userId) { - - List<Position> result = new LinkedList<>(); - - if (Context.getPermissionsManager() != null) { - for (long deviceId : Context.getPermissionsManager().getUserAdmin(userId) - ? getAllUserItems(userId) : getUserItems(userId)) { - if (positions.containsKey(deviceId)) { - result.add(positions.get(deviceId)); - } - } - } - - return result; - } - - @Override - public boolean lookupAttributeBoolean( - long deviceId, String attributeName, boolean defaultValue, boolean lookupServer, boolean lookupConfig) { - Object result = lookupAttribute(deviceId, attributeName, lookupServer, lookupConfig); - if (result != null) { - return result instanceof String ? Boolean.parseBoolean((String) result) : (Boolean) result; - } - return defaultValue; - } - - @Override - public String lookupAttributeString( - long deviceId, String attributeName, String defaultValue, boolean lookupServer, boolean lookupConfig) { - Object result = lookupAttribute(deviceId, attributeName, lookupServer, lookupConfig); - return result != null ? (String) result : defaultValue; - } - - @Override - public int lookupAttributeInteger( - long deviceId, String attributeName, int defaultValue, boolean lookupServer, boolean lookupConfig) { - Object result = lookupAttribute(deviceId, attributeName, lookupServer, lookupConfig); - if (result != null) { - return result instanceof String ? Integer.parseInt((String) result) : ((Number) result).intValue(); - } - return defaultValue; - } - - @Override - public long lookupAttributeLong( - long deviceId, String attributeName, long defaultValue, boolean lookupServer, boolean lookupConfig) { - Object result = lookupAttribute(deviceId, attributeName, lookupServer, lookupConfig); - if (result != null) { - return result instanceof String ? Long.parseLong((String) result) : ((Number) result).longValue(); - } - return defaultValue; - } - - public double lookupAttributeDouble( - long deviceId, String attributeName, double defaultValue, boolean lookupServer, boolean lookupConfig) { - Object result = lookupAttribute(deviceId, attributeName, lookupServer, lookupConfig); - if (result != null) { - return result instanceof String ? Double.parseDouble((String) result) : ((Number) result).doubleValue(); - } - return defaultValue; - } - - private Object lookupAttribute(long deviceId, String attributeName, boolean lookupServer, boolean lookupConfig) { - Object result = null; - Device device = getById(deviceId); - if (device != null) { - result = device.getAttributes().get(attributeName); - if (result == null) { - long groupId = device.getGroupId(); - while (groupId != 0) { - Group group = Context.getGroupsManager().getById(groupId); - if (group != null) { - result = group.getAttributes().get(attributeName); - if (result != null) { - break; - } - groupId = group.getGroupId(); - } else { - groupId = 0; - } - } - } - if (result == null && lookupServer) { - Server server = Context.getPermissionsManager().getServer(); - result = server.getAttributes().get(attributeName); - } - if (result == null && lookupConfig) { - result = Context.getConfig().getString(attributeName); - } - } - return result; - } - - public void resetDeviceAccumulators(DeviceAccumulators deviceAccumulators) throws StorageException { - Position last = positions.get(deviceAccumulators.getDeviceId()); - if (last != null) { - if (deviceAccumulators.getTotalDistance() != null) { - last.getAttributes().put(Position.KEY_TOTAL_DISTANCE, deviceAccumulators.getTotalDistance()); - } - if (deviceAccumulators.getHours() != null) { - last.getAttributes().put(Position.KEY_HOURS, deviceAccumulators.getHours()); - } - getDataManager().addObject(last); - updateLatestPosition(last); - } else { - throw new IllegalArgumentException(); - } - } - - public DeviceState getDeviceState(long deviceId) { - DeviceState deviceState = deviceStates.get(deviceId); - if (deviceState == null) { - deviceState = new DeviceState(); - deviceStates.put(deviceId, deviceState); - } - return deviceState; - } - - public void setDeviceState(long deviceId, DeviceState deviceState) { - deviceStates.put(deviceId, deviceState); - } - -} diff --git a/src/main/java/org/traccar/database/DriversManager.java b/src/main/java/org/traccar/database/DriversManager.java deleted file mode 100644 index d111cd643..000000000 --- a/src/main/java/org/traccar/database/DriversManager.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org) - * Copyright 2017 Andrey Kunitsyn (andrey@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 java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import org.traccar.model.Driver; - -public class DriversManager extends ExtendedObjectManager<Driver> { - - private Map<String, Driver> driversByUniqueId; - - public DriversManager(DataManager dataManager) { - super(dataManager, Driver.class); - try { - writeLock(); - if (driversByUniqueId == null) { - driversByUniqueId = new ConcurrentHashMap<>(); - } - } finally { - writeUnlock(); - } - } - - private void addByUniqueId(Driver driver) { - try { - writeLock(); - if (driversByUniqueId == null) { - driversByUniqueId = new ConcurrentHashMap<>(); - } - driversByUniqueId.put(driver.getUniqueId(), driver); - } finally { - writeUnlock(); - } - } - - private void removeByUniqueId(String driverUniqueId) { - try { - writeLock(); - if (driversByUniqueId == null) { - driversByUniqueId = new ConcurrentHashMap<>(); - } - driversByUniqueId.remove(driverUniqueId); - } finally { - writeUnlock(); - } - } - - @Override - protected void addNewItem(Driver driver) { - super.addNewItem(driver); - addByUniqueId(driver); - } - - @Override - protected void updateCachedItem(Driver driver) { - Driver cachedDriver = getById(driver.getId()); - cachedDriver.setName(driver.getName()); - if (!driver.getUniqueId().equals(cachedDriver.getUniqueId())) { - removeByUniqueId(cachedDriver.getUniqueId()); - cachedDriver.setUniqueId(driver.getUniqueId()); - addByUniqueId(cachedDriver); - } - cachedDriver.setAttributes(driver.getAttributes()); - } - - @Override - protected void removeCachedItem(long driverId) { - Driver cachedDriver = getById(driverId); - if (cachedDriver != null) { - String driverUniqueId = cachedDriver.getUniqueId(); - super.removeCachedItem(driverId); - removeByUniqueId(driverUniqueId); - } - } - - public Driver getDriverByUniqueId(String uniqueId) { - try { - readLock(); - return driversByUniqueId.get(uniqueId); - } finally { - readUnlock(); - } - } -} diff --git a/src/main/java/org/traccar/database/ExtendedObjectManager.java b/src/main/java/org/traccar/database/ExtendedObjectManager.java deleted file mode 100644 index 006ed47b2..000000000 --- a/src/main/java/org/traccar/database/ExtendedObjectManager.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org) - * Copyright 2017 Andrey Kunitsyn (andrey@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 java.util.Collection; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traccar.Context; -import org.traccar.model.Device; -import org.traccar.model.Group; -import org.traccar.model.Permission; -import org.traccar.model.BaseModel; -import org.traccar.storage.StorageException; - -public abstract class ExtendedObjectManager<T extends BaseModel> extends SimpleObjectManager<T> { - - private static final Logger LOGGER = LoggerFactory.getLogger(ExtendedObjectManager.class); - - private final Map<Long, Set<Long>> deviceItems = new ConcurrentHashMap<>(); - private final Map<Long, Set<Long>> deviceItemsWithGroups = new ConcurrentHashMap<>(); - private final Map<Long, Set<Long>> groupItems = new ConcurrentHashMap<>(); - - protected ExtendedObjectManager(DataManager dataManager, Class<T> baseClass) { - super(dataManager, baseClass); - refreshExtendedPermissions(); - } - - public final Set<Long> getGroupItems(long groupId) { - try { - readLock(); - Set<Long> result = groupItems.get(groupId); - if (result != null) { - return new HashSet<>(result); - } else { - return new HashSet<>(); - } - } finally { - readUnlock(); - } - } - - public final Set<Long> getDeviceItems(long deviceId) { - try { - readLock(); - Set<Long> result = deviceItems.get(deviceId); - if (result != null) { - return new HashSet<>(result); - } else { - return new HashSet<>(); - } - } finally { - readUnlock(); - } - } - - public Set<Long> getAllDeviceItems(long deviceId) { - try { - readLock(); - Set<Long> result = deviceItemsWithGroups.get(deviceId); - if (result != null) { - return new HashSet<>(result); - } else { - return new HashSet<>(); - } - } finally { - readUnlock(); - } - } - - @Override - public void removeItem(long itemId) throws StorageException { - super.removeItem(itemId); - refreshExtendedPermissions(); - } - - public void refreshExtendedPermissions() { - if (getDataManager() != null) { - try { - Collection<Permission> databaseGroupPermissions = - getDataManager().getPermissions(Group.class, getBaseClass()); - - Collection<Permission> databaseDevicePermissions = - getDataManager().getPermissions(Device.class, getBaseClass()); - - writeLock(); - - groupItems.clear(); - deviceItems.clear(); - deviceItemsWithGroups.clear(); - - for (Permission groupPermission : databaseGroupPermissions) { - groupItems - .computeIfAbsent(groupPermission.getOwnerId(), key -> new HashSet<>()) - .add(groupPermission.getPropertyId()); - } - - for (Permission devicePermission : databaseDevicePermissions) { - deviceItems - .computeIfAbsent(devicePermission.getOwnerId(), key -> new HashSet<>()) - .add(devicePermission.getPropertyId()); - deviceItemsWithGroups - .computeIfAbsent(devicePermission.getOwnerId(), key -> new HashSet<>()) - .add(devicePermission.getPropertyId()); - } - - for (Device device : Context.getDeviceManager().getAllDevices()) { - long groupId = device.getGroupId(); - while (groupId > 0) { - deviceItemsWithGroups - .computeIfAbsent(device.getId(), key -> new HashSet<>()) - .addAll(groupItems.getOrDefault(groupId, new HashSet<>())); - Group group = Context.getGroupsManager().getById(groupId); - groupId = group != null ? group.getGroupId() : 0; - } - } - - } catch (StorageException | ClassNotFoundException error) { - LOGGER.warn("Refresh permissions error", error); - } finally { - writeUnlock(); - } - } - } -} diff --git a/src/main/java/org/traccar/database/GeofenceManager.java b/src/main/java/org/traccar/database/GeofenceManager.java deleted file mode 100644 index a32847cf9..000000000 --- a/src/main/java/org/traccar/database/GeofenceManager.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2016 - 2017 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 java.util.ArrayList; -import java.util.List; - -import org.traccar.Context; -import org.traccar.model.Device; -import org.traccar.model.Geofence; -import org.traccar.model.Position; - -public class GeofenceManager extends ExtendedObjectManager<Geofence> { - - public GeofenceManager(DataManager dataManager) { - super(dataManager, Geofence.class); - } - - @Override - public final void refreshExtendedPermissions() { - super.refreshExtendedPermissions(); - recalculateDevicesGeofences(); - } - - public List<Long> getCurrentDeviceGeofences(Position position) { - List<Long> result = new ArrayList<>(); - for (long geofenceId : getAllDeviceItems(position.getDeviceId())) { - Geofence geofence = getById(geofenceId); - if (geofence != null && geofence.getGeometry() - .containsPoint(position.getLatitude(), position.getLongitude())) { - result.add(geofenceId); - } - } - return result; - } - - public void recalculateDevicesGeofences() { - for (Device device : Context.getDeviceManager().getAllDevices()) { - List<Long> deviceGeofenceIds = device.getGeofenceIds(); - if (deviceGeofenceIds == null) { - deviceGeofenceIds = new ArrayList<>(); - } else { - deviceGeofenceIds.clear(); - } - Position lastPosition = Context.getIdentityManager().getLastPosition(device.getId()); - if (lastPosition != null && getAllDeviceItems(device.getId()) != null) { - deviceGeofenceIds.addAll(getCurrentDeviceGeofences(lastPosition)); - } - device.setGeofenceIds(deviceGeofenceIds); - } - } - -} diff --git a/src/main/java/org/traccar/database/GroupsManager.java b/src/main/java/org/traccar/database/GroupsManager.java deleted file mode 100644 index dafddc0cc..000000000 --- a/src/main/java/org/traccar/database/GroupsManager.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org) - * Copyright 2017 Andrey Kunitsyn (andrey@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 java.util.HashSet; -import java.util.Set; - -import org.traccar.Context; -import org.traccar.model.Group; -import org.traccar.storage.StorageException; - -public class GroupsManager extends BaseObjectManager<Group> implements ManagableObjects { - - public GroupsManager(DataManager dataManager) { - super(dataManager, Group.class); - } - - private void checkGroupCycles(Group group) { - Set<Long> groups = new HashSet<>(); - while (group != null) { - if (groups.contains(group.getId())) { - throw new IllegalArgumentException("Cycle in group hierarchy"); - } - groups.add(group.getId()); - group = getById(group.getGroupId()); - } - } - - @Override - public Set<Long> getAllItems() { - Set<Long> result = super.getAllItems(); - if (result.isEmpty()) { - refreshItems(); - result = super.getAllItems(); - } - return result; - } - - @Override - protected void addNewItem(Group group) { - checkGroupCycles(group); - super.addNewItem(group); - } - - @Override - public void updateItem(Group group) throws StorageException { - checkGroupCycles(group); - super.updateItem(group); - } - - @Override - public Set<Long> getUserItems(long userId) { - if (Context.getPermissionsManager() != null) { - return Context.getPermissionsManager().getGroupPermissions(userId); - } else { - return new HashSet<>(); - } - } - - @Override - public Set<Long> getManagedItems(long userId) { - Set<Long> result = getUserItems(userId); - for (long managedUserId : Context.getUsersManager().getUserItems(userId)) { - result.addAll(getUserItems(managedUserId)); - } - return result; - } - -} diff --git a/src/main/java/org/traccar/database/IdentityManager.java b/src/main/java/org/traccar/database/IdentityManager.java deleted file mode 100644 index af6a6ce71..000000000 --- a/src/main/java/org/traccar/database/IdentityManager.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2015 - 2019 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.Position; - -public interface IdentityManager { - - long addUnknownDevice(String uniqueId); - - Device getById(long id); - - Device getByUniqueId(String uniqueId) throws Exception; - - String getDevicePassword(long id, String protocol, String defaultPassword); - - Position getLastPosition(long deviceId); - - boolean isLatestPosition(Position position); - - boolean lookupAttributeBoolean( - long deviceId, String attributeName, boolean defaultValue, boolean lookupServer, boolean lookupConfig); - - String lookupAttributeString( - long deviceId, String attributeName, String defaultValue, boolean lookupServer, boolean lookupConfig); - - int lookupAttributeInteger( - long deviceId, String attributeName, int defaultValue, boolean lookupServer, boolean lookupConfig); - - long lookupAttributeLong( - long deviceId, String attributeName, long defaultValue, boolean lookupServer, boolean lookupConfig); - - double lookupAttributeDouble( - long deviceId, String attributeName, double defaultValue, boolean lookupServer, boolean lookupConfig); - -} diff --git a/src/main/java/org/traccar/database/LdapProvider.java b/src/main/java/org/traccar/database/LdapProvider.java index d659a11a1..d517294b8 100644 --- a/src/main/java/org/traccar/database/LdapProvider.java +++ b/src/main/java/org/traccar/database/LdapProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 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. diff --git a/src/main/java/org/traccar/database/MailManager.java b/src/main/java/org/traccar/database/MailManager.java deleted file mode 100644 index d94f55cda..000000000 --- a/src/main/java/org/traccar/database/MailManager.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright 2016 - 2021 Anton Tananaev (anton@traccar.org) - * Copyright 2017 - 2018 Andrey Kunitsyn (andrey@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.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traccar.Context; -import org.traccar.Main; -import org.traccar.model.User; -import org.traccar.notification.PropertiesProvider; - -import javax.mail.BodyPart; -import javax.mail.Message; -import javax.mail.MessagingException; -import javax.mail.Multipart; -import javax.mail.Session; -import javax.mail.Transport; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeBodyPart; -import javax.mail.internet.MimeMessage; -import javax.mail.internet.MimeMultipart; -import java.util.Date; -import java.util.Properties; - -public final class MailManager { - - private static final Logger LOGGER = LoggerFactory.getLogger(MailManager.class); - - private static Properties getProperties(PropertiesProvider provider) { - Properties properties = new Properties(); - String host = provider.getString("mail.smtp.host"); - if (host != null) { - properties.put("mail.transport.protocol", provider.getString("mail.transport.protocol", "smtp")); - properties.put("mail.smtp.host", host); - properties.put("mail.smtp.port", String.valueOf(provider.getInteger("mail.smtp.port", 25))); - - Boolean starttlsEnable = provider.getBoolean("mail.smtp.starttls.enable"); - if (starttlsEnable != null) { - properties.put("mail.smtp.starttls.enable", String.valueOf(starttlsEnable)); - } - Boolean starttlsRequired = provider.getBoolean("mail.smtp.starttls.required"); - if (starttlsRequired != null) { - properties.put("mail.smtp.starttls.required", String.valueOf(starttlsRequired)); - } - - Boolean sslEnable = provider.getBoolean("mail.smtp.ssl.enable"); - if (sslEnable != null) { - properties.put("mail.smtp.ssl.enable", String.valueOf(sslEnable)); - } - String sslTrust = provider.getString("mail.smtp.ssl.trust"); - if (sslTrust != null) { - properties.put("mail.smtp.ssl.trust", sslTrust); - } - - String sslProtocols = provider.getString("mail.smtp.ssl.protocols"); - if (sslProtocols != null) { - properties.put("mail.smtp.ssl.protocols", sslProtocols); - } - - String username = provider.getString("mail.smtp.username"); - if (username != null) { - properties.put("mail.smtp.username", username); - } - String password = provider.getString("mail.smtp.password"); - if (password != null) { - properties.put("mail.smtp.password", password); - } - String from = provider.getString("mail.smtp.from"); - if (from != null) { - properties.put("mail.smtp.from", from); - } - } - return properties; - } - - public boolean getEmailEnabled() { - return Context.getConfig().hasKey("mail.smtp.host"); - } - - public void sendMessage( - long userId, String subject, String body) throws MessagingException { - sendMessage(userId, subject, body, null); - } - - public void sendMessage( - long userId, String subject, String body, MimeBodyPart attachment) throws MessagingException { - User user = Context.getPermissionsManager().getUser(userId); - - Properties properties = null; - if (!Context.getConfig().getBoolean("mail.smtp.ignoreUserConfig")) { - properties = getProperties(new PropertiesProvider(user)); - } - if (properties == null || !properties.containsKey("mail.smtp.host")) { - properties = getProperties(new PropertiesProvider(Context.getConfig())); - } - if (!properties.containsKey("mail.smtp.host")) { - LOGGER.warn("No SMTP configuration found"); - return; - } - - Session session = Session.getInstance(properties); - - MimeMessage message = new MimeMessage(session); - - String from = properties.getProperty("mail.smtp.from"); - if (from != null) { - message.setFrom(new InternetAddress(from)); - } - - message.addRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail())); - message.setSubject(subject); - message.setSentDate(new Date()); - - if (attachment != null) { - Multipart multipart = new MimeMultipart(); - - BodyPart messageBodyPart = new MimeBodyPart(); - messageBodyPart.setContent(body, "text/html; charset=utf-8"); - multipart.addBodyPart(messageBodyPart); - multipart.addBodyPart(attachment); - - message.setContent(multipart); - } else { - message.setContent(body, "text/html; charset=utf-8"); - } - - try (Transport transport = session.getTransport()) { - Main.getInjector().getInstance(StatisticsManager.class).registerMail(); - transport.connect( - properties.getProperty("mail.smtp.host"), - properties.getProperty("mail.smtp.username"), - properties.getProperty("mail.smtp.password")); - transport.sendMessage(message, message.getAllRecipients()); - } - } - -} diff --git a/src/main/java/org/traccar/database/MediaManager.java b/src/main/java/org/traccar/database/MediaManager.java index edade5766..2f2369c96 100644 --- a/src/main/java/org/traccar/database/MediaManager.java +++ b/src/main/java/org/traccar/database/MediaManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 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. @@ -18,10 +18,15 @@ package org.traccar.database; import io.netty.buffer.ByteBuf; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.traccar.config.Config; +import org.traccar.config.Keys; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; @@ -30,14 +35,16 @@ import java.nio.file.Paths; import java.text.SimpleDateFormat; import java.util.Date; +@Singleton public class MediaManager { private static final Logger LOGGER = LoggerFactory.getLogger(MediaManager.class); - private String path; + private final String path; - public MediaManager(String path) { - this.path = path; + @Inject + public MediaManager(Config config) { + this.path = config.getString(Keys.MEDIA_PATH); } private File createFile(String uniqueId, String name) throws IOException { @@ -49,6 +56,10 @@ public class MediaManager { return filePath.toFile(); } + public OutputStream createFileStream(String uniqueId, String name, String extension) throws IOException { + return new FileOutputStream(createFile(uniqueId, name + "." + extension)); + } + public String writeFile(String uniqueId, ByteBuf buf, String extension) { if (path != null) { int size = buf.readableBytes(); diff --git a/src/main/java/org/traccar/database/NotificationManager.java b/src/main/java/org/traccar/database/NotificationManager.java index f358b1d4d..3a57788fb 100644 --- a/src/main/java/org/traccar/database/NotificationManager.java +++ b/src/main/java/org/traccar/database/NotificationManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) * Copyright 2016 - 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,128 +16,141 @@ */ package org.traccar.database; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.Arrays; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.traccar.Context; +import org.traccar.config.Config; import org.traccar.config.Keys; +import org.traccar.forward.EventData; +import org.traccar.forward.EventForwarder; +import org.traccar.geocoder.Geocoder; import org.traccar.model.Calendar; +import org.traccar.model.Device; import org.traccar.model.Event; +import org.traccar.model.Geofence; +import org.traccar.model.Maintenance; import org.traccar.model.Notification; import org.traccar.model.Position; -import org.traccar.model.Typed; +import org.traccar.notification.MessageException; +import org.traccar.notification.NotificatorManager; +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.Request; + +import jakarta.annotation.Nullable; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import java.util.Arrays; +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; -public class NotificationManager extends ExtendedObjectManager<Notification> { +@Singleton +public class NotificationManager { private static final Logger LOGGER = LoggerFactory.getLogger(NotificationManager.class); - private final boolean geocodeOnRequest; + private final Storage storage; + private final CacheManager cacheManager; + private final EventForwarder eventForwarder; + private final NotificatorManager notificatorManager; + private final Geocoder geocoder; - public NotificationManager(DataManager dataManager) { - super(dataManager, Notification.class); - geocodeOnRequest = Context.getConfig().getBoolean(Keys.GEOCODER_ON_REQUEST); - } + private final boolean geocodeOnRequest; - private Set<Long> getEffectiveNotifications(long userId, long deviceId, Date time) { - Set<Long> result = new HashSet<>(); - Set<Long> deviceNotifications = getAllDeviceItems(deviceId); - for (long itemId : getUserItems(userId)) { - if (getById(itemId).getAlways() || deviceNotifications.contains(itemId)) { - long calendarId = getById(itemId).getCalendarId(); - Calendar calendar = calendarId != 0 ? Context.getCalendarManager().getById(calendarId) : null; - if (calendar == null || calendar.checkMoment(time)) { - result.add(itemId); - } - } - } - return result; + @Inject + public NotificationManager( + Config config, Storage storage, CacheManager cacheManager, @Nullable EventForwarder eventForwarder, + NotificatorManager notificatorManager, @Nullable Geocoder geocoder) { + this.storage = storage; + this.cacheManager = cacheManager; + this.eventForwarder = eventForwarder; + this.notificatorManager = notificatorManager; + this.geocoder = geocoder; + geocodeOnRequest = config.getBoolean(Keys.GEOCODER_ON_REQUEST); } - public void updateEvent(Event event, Position position) { + private void updateEvent(Event event, Position position) { try { - getDataManager().addObject(event); + event.setId(storage.addObject(event, new Request(new Columns.Exclude("id")))); } catch (StorageException error) { LOGGER.warn("Event save error", error); } - long deviceId = event.getDeviceId(); - Set<Long> users = Context.getPermissionsManager().getDeviceUsers(deviceId); - Set<Long> usersToForward = null; - if (Context.getEventForwarder() != null) { - usersToForward = new HashSet<>(); - } - for (long userId : users) { - if ((event.getGeofenceId() == 0 - || Context.getGeofenceManager().checkItemPermission(userId, event.getGeofenceId())) - && (event.getMaintenanceId() == 0 - || Context.getMaintenancesManager().checkItemPermission(userId, event.getMaintenanceId()))) { - if (usersToForward != null) { - usersToForward.add(userId); - } - final Set<String> notificators = new HashSet<>(); - for (long notificationId : getEffectiveNotifications(userId, deviceId, event.getEventTime())) { - Notification notification = getById(notificationId); - if (getById(notificationId).getType().equals(event.getType())) { - boolean filter = false; - if (event.getType().equals(Event.TYPE_ALARM)) { - String alarmsAttribute = notification.getString("alarms"); - if (alarmsAttribute == null) { - filter = true; - } else { - List<String> alarms = Arrays.asList(alarmsAttribute.split(",")); - filter = !alarms.contains(event.getString(Position.KEY_ALARM)); - } - } - if (!filter) { - notificators.addAll(notification.getNotificatorsTypes()); + var notifications = cacheManager.getDeviceObjects(event.getDeviceId(), Notification.class).stream() + .filter(notification -> notification.getType().equals(event.getType())) + .filter(notification -> { + if (event.getType().equals(Event.TYPE_ALARM)) { + String alarmsAttribute = notification.getString("alarms"); + if (alarmsAttribute != null) { + return Arrays.asList(alarmsAttribute.split(",")) + .contains(event.getString(Position.KEY_ALARM)); } + return false; } - } - - if (position != null && position.getAddress() == null - && geocodeOnRequest && Context.getGeocoder() != null) { - position.setAddress(Context.getGeocoder() - .getAddress(position.getLatitude(), position.getLongitude(), null)); - } + return true; + }) + .filter(notification -> { + long calendarId = notification.getCalendarId(); + Calendar calendar = calendarId != 0 ? cacheManager.getObject(Calendar.class, calendarId) : null; + return calendar == null || calendar.checkMoment(event.getEventTime()); + }) + .collect(Collectors.toUnmodifiableList()); - for (String notificator : notificators) { - Context.getNotificatorManager().getNotificator(notificator).sendAsync(userId, event, position); - } + if (!notifications.isEmpty()) { + if (position != null && position.getAddress() == null && geocodeOnRequest && geocoder != null) { + position.setAddress(geocoder.getAddress(position.getLatitude(), position.getLongitude(), null)); } + + notifications.forEach(notification -> { + cacheManager.getNotificationUsers(notification.getId(), event.getDeviceId()).forEach(user -> { + for (String notificator : notification.getNotificatorsTypes()) { + try { + notificatorManager.getNotificator(notificator).send(notification, user, event, position); + } catch (MessageException exception) { + LOGGER.warn("Notification failed", exception); + } + } + }); + }); } - if (Context.getEventForwarder() != null) { - Context.getEventForwarder().forwardEvent(event, position, usersToForward); - } + + forwardEvent(event, position); } - public void updateEvents(Map<Event, Position> events) { - for (Entry<Event, Position> event : events.entrySet()) { - updateEvent(event.getKey(), event.getValue()); + private void forwardEvent(Event event, Position position) { + if (eventForwarder != null) { + EventData eventData = new EventData(); + eventData.setEvent(event); + eventData.setPosition(position); + eventData.setDevice(cacheManager.getObject(Device.class, event.getDeviceId())); + if (event.getGeofenceId() != 0) { + eventData.setGeofence(cacheManager.getObject(Geofence.class, event.getGeofenceId())); + } + if (event.getMaintenanceId() != 0) { + eventData.setMaintenance(cacheManager.getObject(Maintenance.class, event.getMaintenanceId())); + } + eventForwarder.forward(eventData, (success, throwable) -> { + if (!success) { + LOGGER.warn("Event forwarding failed", throwable); + } + }); } } - public Set<Typed> getAllNotificationTypes() { - Set<Typed> types = new HashSet<>(); - Field[] fields = Event.class.getDeclaredFields(); - for (Field field : fields) { - if (Modifier.isStatic(field.getModifiers()) && field.getName().startsWith("TYPE_")) { - try { - types.add(new Typed(field.get(null).toString())); - } catch (IllegalArgumentException | IllegalAccessException error) { - LOGGER.warn("Get event types error", error); - } + public void updateEvents(Map<Event, Position> events) { + for (Entry<Event, Position> entry : events.entrySet()) { + Event event = entry.getKey(); + Position position = entry.getValue(); + try { + cacheManager.addDevice(event.getDeviceId()); + updateEvent(event, position); + } catch (StorageException e) { + throw new RuntimeException(e); + } finally { + cacheManager.removeDevice(event.getDeviceId()); } } - return types; } } diff --git a/src/main/java/org/traccar/database/OpenIdProvider.java b/src/main/java/org/traccar/database/OpenIdProvider.java new file mode 100644 index 000000000..312be8890 --- /dev/null +++ b/src/main/java/org/traccar/database/OpenIdProvider.java @@ -0,0 +1,203 @@ +/* + * Copyright 2023 Daniel Raper (me@danr.uk) + * + * 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.config.Config; +import org.traccar.config.Keys; +import org.traccar.api.resource.SessionResource; +import org.traccar.api.security.LoginService; +import org.traccar.model.User; +import org.traccar.storage.StorageException; +import org.traccar.helper.LogAction; +import org.traccar.helper.WebHelper; + +import java.net.URI; +import java.net.URISyntaxException; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse.BodyHandlers; +import java.security.GeneralSecurityException; +import java.util.List; +import java.util.Map; +import java.io.IOException; +import jakarta.servlet.http.HttpServletRequest; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.inject.Inject; + +import com.nimbusds.oauth2.sdk.http.HTTPResponse; +import com.nimbusds.oauth2.sdk.AuthorizationCode; +import com.nimbusds.oauth2.sdk.ResponseType; +import com.nimbusds.oauth2.sdk.Scope; +import com.nimbusds.oauth2.sdk.AuthorizationGrant; +import com.nimbusds.oauth2.sdk.TokenRequest; +import com.nimbusds.oauth2.sdk.TokenResponse; +import com.nimbusds.oauth2.sdk.AuthorizationCodeGrant; +import com.nimbusds.oauth2.sdk.ParseException; +import com.nimbusds.oauth2.sdk.AuthorizationResponse; +import com.nimbusds.oauth2.sdk.auth.Secret; +import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic; +import com.nimbusds.oauth2.sdk.auth.ClientAuthentication; +import com.nimbusds.oauth2.sdk.token.BearerAccessToken; +import com.nimbusds.oauth2.sdk.id.State; +import com.nimbusds.oauth2.sdk.id.ClientID; +import com.nimbusds.openid.connect.sdk.OIDCTokenResponse; +import com.nimbusds.openid.connect.sdk.OIDCTokenResponseParser; +import com.nimbusds.openid.connect.sdk.UserInfoResponse; +import com.nimbusds.openid.connect.sdk.UserInfoRequest; +import com.nimbusds.openid.connect.sdk.AuthenticationRequest; +import com.nimbusds.openid.connect.sdk.claims.UserInfo; + +public class OpenIdProvider { + private final Boolean force; + private final ClientID clientId; + private final ClientAuthentication clientAuth; + private URI callbackUrl; + private URI authUrl; + private URI tokenUrl; + private URI userInfoUrl; + private URI baseUrl; + private final String adminGroup; + private final String allowGroup; + + private LoginService loginService; + + @Inject + public OpenIdProvider( + Config config, LoginService loginService, HttpClient httpClient, ObjectMapper objectMapper + ) throws InterruptedException, IOException, URISyntaxException { + this.loginService = loginService; + + force = config.getBoolean(Keys.OPENID_FORCE); + clientId = new ClientID(config.getString(Keys.OPENID_CLIENT_ID)); + clientAuth = new ClientSecretBasic(clientId, new Secret(config.getString(Keys.OPENID_CLIENT_SECRET))); + + baseUrl = new URI(WebHelper.retrieveWebUrl(config)); + callbackUrl = new URI(WebHelper.retrieveWebUrl(config) + "/api/session/openid/callback"); + + if (config.hasKey(Keys.OPENID_ISSUER_URL)) { + HttpRequest httpRequest = HttpRequest.newBuilder( + URI.create(config.getString(Keys.OPENID_ISSUER_URL) + "/.well-known/openid-configuration")) + .header("Accept", "application/json") + .build(); + + String httpResponse = httpClient.send(httpRequest, BodyHandlers.ofString()).body(); + + Map<String, Object> discoveryMap = objectMapper.readValue( + httpResponse, new TypeReference<Map<String, Object>>() { }); + + authUrl = new URI((String) discoveryMap.get("authorization_endpoint")); + tokenUrl = new URI((String) discoveryMap.get("token_endpoint")); + userInfoUrl = new URI((String) discoveryMap.get("userinfo_endpoint")); + } else { + authUrl = new URI(config.getString(Keys.OPENID_AUTH_URL)); + tokenUrl = new URI(config.getString(Keys.OPENID_TOKEN_URL)); + userInfoUrl = new URI(config.getString(Keys.OPENID_USERINFO_URL)); + } + + adminGroup = config.getString(Keys.OPENID_ADMIN_GROUP); + allowGroup = config.getString(Keys.OPENID_ALLOW_GROUP); + } + + public URI createAuthUri() { + Scope scope = new Scope("openid", "profile", "email"); + + if (adminGroup != null) { + scope.add("groups"); + } + + AuthenticationRequest.Builder request = new AuthenticationRequest.Builder( + new ResponseType("code"), + scope, + clientId, + callbackUrl); + + return request.endpointURI(authUrl) + .state(new State()) + .build() + .toURI(); + } + + private OIDCTokenResponse getToken( + AuthorizationCode code) throws IOException, ParseException, GeneralSecurityException { + AuthorizationGrant codeGrant = new AuthorizationCodeGrant(code, callbackUrl); + TokenRequest tokenRequest = new TokenRequest(tokenUrl, clientAuth, codeGrant); + + HTTPResponse tokenResponse = tokenRequest.toHTTPRequest().send(); + TokenResponse token = OIDCTokenResponseParser.parse(tokenResponse); + if (!token.indicatesSuccess()) { + throw new GeneralSecurityException("Unable to authenticate with the OpenID Connect provider."); + } + + return (OIDCTokenResponse) token.toSuccessResponse(); + } + + private UserInfo getUserInfo(BearerAccessToken token) throws IOException, ParseException, GeneralSecurityException { + HTTPResponse httpResponse = new UserInfoRequest(userInfoUrl, token) + .toHTTPRequest() + .send(); + + UserInfoResponse userInfoResponse = UserInfoResponse.parse(httpResponse); + + if (!userInfoResponse.indicatesSuccess()) { + throw new GeneralSecurityException( + "Failed to access OpenID Connect user info endpoint. Please contact your administrator."); + } + + return userInfoResponse.toSuccessResponse().getUserInfo(); + } + + public URI handleCallback( + URI requestUri, HttpServletRequest request + ) throws StorageException, ParseException, IOException, GeneralSecurityException { + AuthorizationResponse response = AuthorizationResponse.parse(requestUri); + + if (!response.indicatesSuccess()) { + throw new GeneralSecurityException(response.toErrorResponse().getErrorObject().getDescription()); + } + + AuthorizationCode authCode = response.toSuccessResponse().getAuthorizationCode(); + + if (authCode == null) { + throw new GeneralSecurityException("Malformed OpenID callback."); + } + + OIDCTokenResponse tokens = getToken(authCode); + + BearerAccessToken bearerToken = tokens.getOIDCTokens().getBearerAccessToken(); + + UserInfo userInfo = getUserInfo(bearerToken); + + List<String> userGroups = userInfo.getStringListClaim("groups"); + Boolean administrator = adminGroup != null && userGroups.contains(adminGroup); + + if (!(administrator || allowGroup == null || userGroups.contains(allowGroup))) { + throw new GeneralSecurityException("Your OpenID Groups do not permit access to Traccar."); + } + + User user = loginService.login(userInfo.getEmailAddress(), userInfo.getName(), administrator); + + request.getSession().setAttribute(SessionResource.USER_ID_KEY, user.getId()); + LogAction.login(user.getId(), WebHelper.retrieveRemoteAddress(request)); + + return baseUrl; + } + + public boolean getForce() { + return force; + } +} diff --git a/src/main/java/org/traccar/database/PermissionsManager.java b/src/main/java/org/traccar/database/PermissionsManager.java deleted file mode 100644 index 2bb808033..000000000 --- a/src/main/java/org/traccar/database/PermissionsManager.java +++ /dev/null @@ -1,528 +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.database; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traccar.Context; -import org.traccar.model.Attribute; -import org.traccar.model.BaseModel; -import org.traccar.model.Calendar; -import org.traccar.model.Command; -import org.traccar.model.Device; -import org.traccar.model.Driver; -import org.traccar.model.Geofence; -import org.traccar.model.Group; -import org.traccar.model.Maintenance; -import org.traccar.model.ManagedUser; -import org.traccar.model.Notification; -import org.traccar.model.Order; -import org.traccar.model.Permission; -import org.traccar.model.Server; -import org.traccar.model.User; -import org.traccar.storage.StorageException; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -public class PermissionsManager { - - private static final Logger LOGGER = LoggerFactory.getLogger(PermissionsManager.class); - - private final DataManager dataManager; - private final UsersManager usersManager; - - private volatile Server server; - - private final ReadWriteLock lock = new ReentrantReadWriteLock(); - - private final Map<Long, Set<Long>> groupPermissions = new HashMap<>(); - private final Map<Long, Set<Long>> devicePermissions = new HashMap<>(); - private final Map<Long, Set<Long>> deviceUsers = new HashMap<>(); - private final Map<Long, Set<Long>> groupDevices = new HashMap<>(); - - public PermissionsManager(DataManager dataManager, UsersManager usersManager) { - this.dataManager = dataManager; - this.usersManager = usersManager; - refreshServer(); - refreshDeviceAndGroupPermissions(); - } - - protected final void readLock() { - lock.readLock().lock(); - } - - protected final void readUnlock() { - lock.readLock().unlock(); - } - - protected final void writeLock() { - lock.writeLock().lock(); - } - - protected final void writeUnlock() { - lock.writeLock().unlock(); - } - - public User getUser(long userId) { - readLock(); - try { - return usersManager.getById(userId); - } finally { - readUnlock(); - } - } - - public Set<Long> getGroupPermissions(long userId) { - readLock(); - try { - if (!groupPermissions.containsKey(userId)) { - groupPermissions.put(userId, new HashSet<>()); - } - return groupPermissions.get(userId); - } finally { - readUnlock(); - } - } - - public Set<Long> getDevicePermissions(long userId) { - readLock(); - try { - if (!devicePermissions.containsKey(userId)) { - devicePermissions.put(userId, new HashSet<>()); - } - return devicePermissions.get(userId); - } finally { - readUnlock(); - } - } - - private Set<Long> getAllDeviceUsers(long deviceId) { - readLock(); - try { - if (!deviceUsers.containsKey(deviceId)) { - deviceUsers.put(deviceId, new HashSet<>()); - } - return deviceUsers.get(deviceId); - } finally { - readUnlock(); - } - } - - public Set<Long> getDeviceUsers(long deviceId) { - Device device = Context.getIdentityManager().getById(deviceId); - if (device != null && !device.getDisabled()) { - return getAllDeviceUsers(deviceId); - } else { - Set<Long> result = new HashSet<>(); - for (long userId : getAllDeviceUsers(deviceId)) { - if (getUserAdmin(userId)) { - result.add(userId); - } - } - return result; - } - } - - public Set<Long> getGroupDevices(long groupId) { - readLock(); - try { - if (!groupDevices.containsKey(groupId)) { - groupDevices.put(groupId, new HashSet<>()); - } - return groupDevices.get(groupId); - } finally { - readUnlock(); - } - } - - public void refreshServer() { - try { - server = dataManager.getServer(); - } catch (StorageException error) { - LOGGER.warn("Refresh server config error", error); - } - } - - public final void refreshDeviceAndGroupPermissions() { - writeLock(); - try { - groupPermissions.clear(); - devicePermissions.clear(); - try { - GroupTree groupTree = new GroupTree(Context.getGroupsManager().getItems( - Context.getGroupsManager().getAllItems()), - Context.getDeviceManager().getAllDevices()); - for (Permission groupPermission : dataManager.getPermissions(User.class, Group.class)) { - Set<Long> userGroupPermissions = getGroupPermissions(groupPermission.getOwnerId()); - Set<Long> userDevicePermissions = getDevicePermissions(groupPermission.getOwnerId()); - userGroupPermissions.add(groupPermission.getPropertyId()); - for (Group group : groupTree.getGroups(groupPermission.getPropertyId())) { - userGroupPermissions.add(group.getId()); - } - for (Device device : groupTree.getDevices(groupPermission.getPropertyId())) { - userDevicePermissions.add(device.getId()); - } - } - - for (Permission devicePermission : dataManager.getPermissions(User.class, Device.class)) { - getDevicePermissions(devicePermission.getOwnerId()).add(devicePermission.getPropertyId()); - } - - groupDevices.clear(); - for (long groupId : Context.getGroupsManager().getAllItems()) { - for (Device device : groupTree.getDevices(groupId)) { - getGroupDevices(groupId).add(device.getId()); - } - } - - } catch (StorageException | ClassNotFoundException error) { - LOGGER.warn("Refresh device permissions error", error); - } - - deviceUsers.clear(); - for (Map.Entry<Long, Set<Long>> entry : devicePermissions.entrySet()) { - for (long deviceId : entry.getValue()) { - getAllDeviceUsers(deviceId).add(entry.getKey()); - } - } - } finally { - writeUnlock(); - } - } - - public boolean getUserAdmin(long userId) { - User user = getUser(userId); - return user != null && user.getAdministrator(); - } - - public void checkAdmin(long userId) throws SecurityException { - if (!getUserAdmin(userId)) { - throw new SecurityException("Admin access required"); - } - } - - public boolean getUserManager(long userId) { - User user = getUser(userId); - return user != null && user.getUserLimit() != 0; - } - - public void checkManager(long userId) throws SecurityException { - if (!getUserManager(userId)) { - throw new SecurityException("Manager access required"); - } - } - - public void checkManager(long userId, long managedUserId) throws SecurityException { - checkManager(userId); - if (!usersManager.getUserItems(userId).contains(managedUserId)) { - throw new SecurityException("User access denied"); - } - } - - public void checkUserLimit(long userId) throws SecurityException { - int userLimit = getUser(userId).getUserLimit(); - if (userLimit != -1 && usersManager.getUserItems(userId).size() >= userLimit) { - throw new SecurityException("Manager user limit reached"); - } - } - - public void checkDeviceLimit(long userId) throws SecurityException { - int deviceLimit = getUser(userId).getDeviceLimit(); - if (deviceLimit != -1) { - int deviceCount; - if (getUserManager(userId)) { - deviceCount = Context.getDeviceManager().getAllManagedItems(userId).size(); - } else { - deviceCount = Context.getDeviceManager().getAllUserItems(userId).size(); - } - if (deviceCount >= deviceLimit) { - throw new SecurityException("User device limit reached"); - } - } - } - - public boolean getUserReadonly(long userId) { - User user = getUser(userId); - return user != null && user.getReadonly(); - } - - public boolean getUserDeviceReadonly(long userId) { - User user = getUser(userId); - return user != null && user.getDeviceReadonly(); - } - - public boolean getUserLimitCommands(long userId) { - User user = getUser(userId); - return user != null && user.getLimitCommands(); - } - - public boolean getUserDisableReport(long userId) { - User user = getUser(userId); - return user != null && user.getDisableReports(); - } - - public void checkReadonly(long userId) throws SecurityException { - if (!getUserAdmin(userId) && (server.getReadonly() || getUserReadonly(userId))) { - throw new SecurityException("Account is readonly"); - } - } - - public void checkDeviceReadonly(long userId) throws SecurityException { - if (!getUserAdmin(userId) && (server.getDeviceReadonly() || getUserDeviceReadonly(userId))) { - throw new SecurityException("Account is device readonly"); - } - } - - public void checkLimitCommands(long userId) throws SecurityException { - if (!getUserAdmin(userId) && (server.getLimitCommands() || getUserLimitCommands(userId))) { - throw new SecurityException("Account has limit sending commands"); - } - } - - public void checkDisableReports(long userId) throws SecurityException { - if (!getUserAdmin(userId) && (server.getDisableReports() || getUserDisableReport(userId))) { - throw new SecurityException("Account has reports disabled"); - } - } - - public void checkUserDeviceCommand(long userId, long deviceId, long commandId) throws SecurityException { - if (!getUserAdmin(userId) && Context.getCommandsManager().checkDeviceCommand(deviceId, commandId)) { - throw new SecurityException("Command can not be sent to this device"); - } - } - - public void checkUserEnabled(long userId) throws SecurityException { - User user = getUser(userId); - if (user == null) { - throw new SecurityException("Unknown account"); - } - if (user.getDisabled()) { - throw new SecurityException("Account is disabled"); - } - if (user.getExpirationTime() != null && System.currentTimeMillis() > user.getExpirationTime().getTime()) { - throw new SecurityException("Account has expired"); - } - } - - public void checkUserUpdate(long userId, User before, User after) throws SecurityException { - if (before.getAdministrator() != after.getAdministrator() - || before.getDeviceLimit() != after.getDeviceLimit() - || before.getUserLimit() != after.getUserLimit()) { - checkAdmin(userId); - } - User user = getUser(userId); - if (user != null && user.getExpirationTime() != null - && (after.getExpirationTime() == null - || user.getExpirationTime().compareTo(after.getExpirationTime()) < 0)) { - checkAdmin(userId); - } - if (before.getReadonly() != after.getReadonly() - || before.getDeviceReadonly() != after.getDeviceReadonly() - || before.getDisabled() != after.getDisabled() - || before.getLimitCommands() != after.getLimitCommands() - || before.getDisableReports() != after.getDisableReports()) { - if (userId == after.getId()) { - checkAdmin(userId); - } - if (!getUserAdmin(userId)) { - checkManager(userId); - } - } - } - - public void checkUser(long userId, long managedUserId) throws SecurityException { - if (userId != managedUserId && !getUserAdmin(userId)) { - checkManager(userId, managedUserId); - } - } - - public void checkGroup(long userId, long groupId) throws SecurityException { - if (!getGroupPermissions(userId).contains(groupId) && !getUserAdmin(userId)) { - checkManager(userId); - for (long managedUserId : usersManager.getUserItems(userId)) { - if (getGroupPermissions(managedUserId).contains(groupId)) { - return; - } - } - throw new SecurityException("Group access denied"); - } - } - - public void checkDevice(long userId, long deviceId) throws SecurityException { - if (!Context.getDeviceManager().getUserItems(userId).contains(deviceId) && !getUserAdmin(userId)) { - checkManager(userId); - for (long managedUserId : usersManager.getUserItems(userId)) { - if (Context.getDeviceManager().getUserItems(managedUserId).contains(deviceId)) { - return; - } - } - throw new SecurityException("Device access denied"); - } - } - - public void checkRegistration(long userId) { - if (!server.getRegistration() && !getUserAdmin(userId)) { - throw new SecurityException("Registration disabled"); - } - } - - public void checkPermission(Class<?> object, long userId, long objectId) - throws SecurityException { - SimpleObjectManager<? extends BaseModel> manager = null; - - if (object.equals(Device.class)) { - checkDevice(userId, objectId); - } else if (object.equals(Group.class)) { - checkGroup(userId, objectId); - } else if (object.equals(User.class) || object.equals(ManagedUser.class)) { - checkUser(userId, objectId); - } else if (object.equals(Geofence.class)) { - manager = Context.getGeofenceManager(); - } else if (object.equals(Attribute.class)) { - manager = Context.getAttributesManager(); - } else if (object.equals(Driver.class)) { - manager = Context.getDriversManager(); - } else if (object.equals(Calendar.class)) { - manager = Context.getCalendarManager(); - } else if (object.equals(Command.class)) { - manager = Context.getCommandsManager(); - } else if (object.equals(Maintenance.class)) { - manager = Context.getMaintenancesManager(); - } else if (object.equals(Notification.class)) { - manager = Context.getNotificationManager(); - } else if (object.equals(Order.class)) { - manager = Context.getOrderManager(); - } else { - throw new IllegalArgumentException("Unknown object type"); - } - - if (manager != null && !manager.checkItemPermission(userId, objectId) && !getUserAdmin(userId)) { - checkManager(userId); - for (long managedUserId : usersManager.getManagedItems(userId)) { - if (manager.checkItemPermission(managedUserId, objectId)) { - return; - } - } - throw new SecurityException("Type " + object + " access denied"); - } - } - - public void refreshAllUsersPermissions() { - if (Context.getGeofenceManager() != null) { - Context.getGeofenceManager().refreshUserItems(); - } - Context.getCalendarManager().refreshUserItems(); - Context.getDriversManager().refreshUserItems(); - Context.getAttributesManager().refreshUserItems(); - Context.getCommandsManager().refreshUserItems(); - Context.getMaintenancesManager().refreshUserItems(); - if (Context.getNotificationManager() != null) { - Context.getNotificationManager().refreshUserItems(); - } - } - - public void refreshAllExtendedPermissions() { - if (Context.getGeofenceManager() != null) { - Context.getGeofenceManager().refreshExtendedPermissions(); - } - Context.getDriversManager().refreshExtendedPermissions(); - Context.getAttributesManager().refreshExtendedPermissions(); - Context.getCommandsManager().refreshExtendedPermissions(); - Context.getMaintenancesManager().refreshExtendedPermissions(); - } - - public void refreshPermissions(Permission permission) { - if (permission.getOwnerClass().equals(User.class)) { - if (permission.getPropertyClass().equals(Device.class) - || permission.getPropertyClass().equals(Group.class)) { - refreshDeviceAndGroupPermissions(); - refreshAllExtendedPermissions(); - } else if (permission.getPropertyClass().equals(ManagedUser.class)) { - usersManager.refreshUserItems(); - } else if (permission.getPropertyClass().equals(Geofence.class) && Context.getGeofenceManager() != null) { - Context.getGeofenceManager().refreshUserItems(); - } else if (permission.getPropertyClass().equals(Driver.class)) { - Context.getDriversManager().refreshUserItems(); - } else if (permission.getPropertyClass().equals(Attribute.class)) { - Context.getAttributesManager().refreshUserItems(); - } else if (permission.getPropertyClass().equals(Calendar.class)) { - Context.getCalendarManager().refreshUserItems(); - } else if (permission.getPropertyClass().equals(Command.class)) { - Context.getCommandsManager().refreshUserItems(); - } else if (permission.getPropertyClass().equals(Maintenance.class)) { - Context.getMaintenancesManager().refreshUserItems(); - } else if (permission.getPropertyClass().equals(Order.class)) { - Context.getOrderManager().refreshUserItems(); - } else if (permission.getPropertyClass().equals(Notification.class) - && Context.getNotificationManager() != null) { - Context.getNotificationManager().refreshUserItems(); - } - } else if (permission.getOwnerClass().equals(Device.class) || permission.getOwnerClass().equals(Group.class)) { - if (permission.getPropertyClass().equals(Geofence.class) && Context.getGeofenceManager() != null) { - Context.getGeofenceManager().refreshExtendedPermissions(); - } else if (permission.getPropertyClass().equals(Driver.class)) { - Context.getDriversManager().refreshExtendedPermissions(); - } else if (permission.getPropertyClass().equals(Attribute.class)) { - Context.getAttributesManager().refreshExtendedPermissions(); - } else if (permission.getPropertyClass().equals(Command.class)) { - Context.getCommandsManager().refreshExtendedPermissions(); - } else if (permission.getPropertyClass().equals(Maintenance.class)) { - Context.getMaintenancesManager().refreshExtendedPermissions(); - } else if (permission.getPropertyClass().equals(Order.class)) { - Context.getOrderManager().refreshExtendedPermissions(); - } else if (permission.getPropertyClass().equals(Notification.class) - && Context.getNotificationManager() != null) { - Context.getNotificationManager().refreshExtendedPermissions(); - } - } - } - - public Server getServer() { - return server; - } - - public void updateServer(Server server) throws StorageException { - dataManager.updateObject(server); - this.server = server; - } - - public User login(String email, String password) throws StorageException { - User user = dataManager.login(email, password); - if (user != null) { - checkUserEnabled(user.getId()); - return getUser(user.getId()); - } - return null; - } - - public Object lookupAttribute(long userId, String key, Object defaultValue) { - Object preference; - Object serverPreference = server.getAttributes().get(key); - Object userPreference = getUser(userId).getAttributes().get(key); - if (server.getForceSettings()) { - preference = serverPreference != null ? serverPreference : userPreference; - } else { - preference = userPreference != null ? userPreference : serverPreference; - } - return preference != null ? preference : defaultValue; - } - -} diff --git a/src/main/java/org/traccar/database/SimpleObjectManager.java b/src/main/java/org/traccar/database/SimpleObjectManager.java deleted file mode 100644 index 78701720f..000000000 --- a/src/main/java/org/traccar/database/SimpleObjectManager.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org) - * Copyright 2017 Andrey Kunitsyn (andrey@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 java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traccar.Context; -import org.traccar.model.BaseModel; -import org.traccar.model.Permission; -import org.traccar.model.User; -import org.traccar.storage.StorageException; - -public abstract class SimpleObjectManager<T extends BaseModel> extends BaseObjectManager<T> - implements ManagableObjects { - - private static final Logger LOGGER = LoggerFactory.getLogger(SimpleObjectManager.class); - - private Map<Long, Set<Long>> userItems; - - protected SimpleObjectManager(DataManager dataManager, Class<T> baseClass) { - super(dataManager, baseClass); - } - - @Override - public final Set<Long> getUserItems(long userId) { - try { - readLock(); - Set<Long> result = userItems.get(userId); - if (result != null) { - return new HashSet<>(result); - } else { - return new HashSet<>(); - } - } finally { - readUnlock(); - } - } - - @Override - public Set<Long> getManagedItems(long userId) { - Set<Long> result = getUserItems(userId); - for (long managedUserId : Context.getUsersManager().getUserItems(userId)) { - result.addAll(getUserItems(managedUserId)); - } - return result; - } - - public final boolean checkItemPermission(long userId, long itemId) { - return getUserItems(userId).contains(itemId); - } - - @Override - public void refreshItems() { - super.refreshItems(); - refreshUserItems(); - } - - public final void refreshUserItems() { - if (getDataManager() != null) { - try { - writeLock(); - userItems = new ConcurrentHashMap<>(); - for (Permission permission : getDataManager().getPermissions(User.class, getBaseClass())) { - Set<Long> items = userItems.computeIfAbsent(permission.getOwnerId(), key -> new HashSet<>()); - items.add(permission.getPropertyId()); - } - } catch (StorageException | ClassNotFoundException error) { - LOGGER.warn("Error getting permissions", error); - } finally { - writeUnlock(); - } - } - } - - @Override - public void removeItem(long itemId) throws StorageException { - super.removeItem(itemId); - refreshUserItems(); - } - -} diff --git a/src/main/java/org/traccar/database/StatisticsManager.java b/src/main/java/org/traccar/database/StatisticsManager.java index 3579ce7a5..445e53e7c 100644 --- a/src/main/java/org/traccar/database/StatisticsManager.java +++ b/src/main/java/org/traccar/database/StatisticsManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,16 +19,21 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.traccar.api.security.ServiceAccountUser; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.helper.DateUtil; import org.traccar.model.Statistics; +import org.traccar.storage.Storage; import org.traccar.storage.StorageException; - -import javax.inject.Inject; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.Entity; -import javax.ws.rs.core.Form; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Request; + +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.core.Form; import java.util.Calendar; import java.util.Date; import java.util.HashMap; @@ -37,6 +42,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; +@Singleton public class StatisticsManager { private static final Logger LOGGER = LoggerFactory.getLogger(StatisticsManager.class); @@ -44,7 +50,7 @@ public class StatisticsManager { private static final int SPLIT_MODE = Calendar.DAY_OF_MONTH; private final Config config; - private final DataManager dataManager; + private final Storage storage; private final Client client; private final ObjectMapper objectMapper; @@ -52,6 +58,7 @@ public class StatisticsManager { private final Set<Long> users = new HashSet<>(); private final Map<Long, String> deviceProtocols = new HashMap<>(); + private final Map<Long, Integer> deviceMessages = new HashMap<>(); private int requests; private int messagesReceived; @@ -62,9 +69,9 @@ public class StatisticsManager { private int geolocationRequests; @Inject - public StatisticsManager(Config config, DataManager dataManager, Client client, ObjectMapper objectMapper) { + public StatisticsManager(Config config, Storage storage, Client client, ObjectMapper objectMapper) { this.config = config; - this.dataManager = dataManager; + this.storage = storage; this.client = client; this.objectMapper = objectMapper; } @@ -93,8 +100,11 @@ public class StatisticsManager { statistics.setProtocols(protocols); } + statistics.set("modern", config.getString(Keys.WEB_PATH).contains("modern")); + users.clear(); deviceProtocols.clear(); + deviceMessages.clear(); requests = 0; messagesReceived = 0; messagesStored = 0; @@ -105,7 +115,7 @@ public class StatisticsManager { } try { - dataManager.addObject(statistics); + storage.addObject(statistics, new Request(new Columns.Exclude("id"))); } catch (StorageException e) { LOGGER.warn("Error saving statistics", e); } @@ -133,6 +143,13 @@ public class StatisticsManager { LOGGER.warn("Failed to serialize protocols", e); } } + if (!statistics.getAttributes().isEmpty()) { + try { + form.param("attributes", objectMapper.writeValueAsString(statistics.getAttributes())); + } catch (JsonProcessingException e) { + LOGGER.warn("Failed to serialize attributes", e); + } + } client.target(url).request().async().post(Entity.form(form)); } @@ -142,7 +159,7 @@ public class StatisticsManager { public synchronized void registerRequest(long userId) { checkSplit(); requests += 1; - if (userId != 0) { + if (userId != 0 && userId != ServiceAccountUser.ID) { users.add(userId); } } @@ -157,9 +174,14 @@ public class StatisticsManager { messagesStored += 1; if (deviceId != 0) { deviceProtocols.put(deviceId, protocol); + deviceMessages.merge(deviceId, 1, Integer::sum); } } + public synchronized int messageStoredCount(long deviceId) { + return deviceMessages.getOrDefault(deviceId, 0); + } + public synchronized void registerMail() { checkSplit(); mailSent += 1; diff --git a/src/main/java/org/traccar/database/UsersManager.java b/src/main/java/org/traccar/database/UsersManager.java deleted file mode 100644 index 31759dc8b..000000000 --- a/src/main/java/org/traccar/database/UsersManager.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org) - * Copyright 2017 Andrey Kunitsyn (andrey@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 java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -import org.traccar.model.User; -import org.traccar.storage.StorageException; - -public class UsersManager extends SimpleObjectManager<User> { - - private Map<String, User> usersTokens; - - public UsersManager(DataManager dataManager) { - super(dataManager, User.class); - if (usersTokens == null) { - usersTokens = new ConcurrentHashMap<>(); - } - } - - private void putToken(User user) { - if (usersTokens == null) { - usersTokens = new ConcurrentHashMap<>(); - } - if (user.getToken() != null) { - usersTokens.put(user.getToken(), user); - } - } - - @Override - protected void addNewItem(User user) { - super.addNewItem(user); - putToken(user); - } - - @Override - protected void updateCachedItem(User user) { - User cachedUser = getById(user.getId()); - super.updateCachedItem(user); - putToken(user); - if (cachedUser.getToken() != null && !cachedUser.getToken().equals(user.getToken())) { - usersTokens.remove(cachedUser.getToken()); - } - } - - @Override - public void updateItem(User user) throws StorageException { - if (user.getHashedPassword() != null) { - getDataManager().updateUserPassword(user); - } - super.updateItem(user); - } - - @Override - protected void removeCachedItem(long userId) { - User cachedUser = getById(userId); - if (cachedUser != null) { - String userToken = cachedUser.getToken(); - super.removeCachedItem(userId); - if (userToken != null) { - usersTokens.remove(userToken); - } - } - } - - @Override - public Set<Long> getManagedItems(long userId) { - Set<Long> result = getUserItems(userId); - result.add(userId); - return result; - } - - public User getUserByToken(String token) { - return usersTokens.get(token); - } - -} diff --git a/src/main/java/org/traccar/forward/AmqpClient.java b/src/main/java/org/traccar/forward/AmqpClient.java new file mode 100644 index 000000000..361cfffee --- /dev/null +++ b/src/main/java/org/traccar/forward/AmqpClient.java @@ -0,0 +1,58 @@ +/* + * 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.forward; + +import com.rabbitmq.client.BuiltinExchangeType; +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; +import com.rabbitmq.client.MessageProperties; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.util.concurrent.TimeoutException; + +public class AmqpClient { + private final Channel channel; + private final String exchange; + private final String topic; + + AmqpClient(String connectionUrl, String exchange, String topic) { + this.exchange = exchange; + this.topic = topic; + + ConnectionFactory factory = new ConnectionFactory(); + try { + factory.setUri(connectionUrl); + } catch (NoSuchAlgorithmException | URISyntaxException | KeyManagementException e) { + throw new RuntimeException("Error while setting URI for RabbitMQ connection factory", e); + } + + try { + Connection connection = factory.newConnection(); + channel = connection.createChannel(); + channel.exchangeDeclare(exchange, BuiltinExchangeType.TOPIC, true); + } catch (IOException | TimeoutException e) { + throw new RuntimeException("Error while creating and configuring RabbitMQ channel", e); + } + } + + public void publishMessage(String message) throws IOException { + channel.basicPublish(exchange, topic, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes()); + } +} diff --git a/src/main/java/org/traccar/forward/EventData.java b/src/main/java/org/traccar/forward/EventData.java new file mode 100644 index 000000000..4471b10b3 --- /dev/null +++ b/src/main/java/org/traccar/forward/EventData.java @@ -0,0 +1,78 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.forward; + +import com.fasterxml.jackson.annotation.JsonInclude; +import org.traccar.model.Device; +import org.traccar.model.Event; +import org.traccar.model.Geofence; +import org.traccar.model.Maintenance; +import org.traccar.model.Position; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class EventData { + + private Event event; + + public Event getEvent() { + return event; + } + + public void setEvent(Event event) { + this.event = event; + } + + private Position position; + + public Position getPosition() { + return position; + } + + public void setPosition(Position position) { + this.position = position; + } + + private Device device; + + public Device getDevice() { + return device; + } + + public void setDevice(Device device) { + this.device = device; + } + + private Geofence geofence; + + public Geofence getGeofence() { + return geofence; + } + + public void setGeofence(Geofence geofence) { + this.geofence = geofence; + } + + private Maintenance maintenance; + + public Maintenance getMaintenance() { + return maintenance; + } + + public void setMaintenance(Maintenance maintenance) { + this.maintenance = maintenance; + } + +} diff --git a/src/main/java/org/traccar/database/ManagableObjects.java b/src/main/java/org/traccar/forward/EventForwarder.java index ec9549493..1f991c0b5 100644 --- a/src/main/java/org/traccar/database/ManagableObjects.java +++ b/src/main/java/org/traccar/forward/EventForwarder.java @@ -1,6 +1,5 @@ /* - * Copyright 2017 Anton Tananaev (anton@traccar.org) - * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org) + * Copyright 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,14 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.traccar.database; - -import java.util.Set; - -public interface ManagableObjects { - - Set<Long> getUserItems(long userId); - - Set<Long> getManagedItems(long userId); +package org.traccar.forward; +public interface EventForwarder { + void forward(EventData eventData, ResultHandler resultHandler); } diff --git a/src/main/java/org/traccar/forward/EventForwarderAmqp.java b/src/main/java/org/traccar/forward/EventForwarderAmqp.java new file mode 100644 index 000000000..5c38a4459 --- /dev/null +++ b/src/main/java/org/traccar/forward/EventForwarderAmqp.java @@ -0,0 +1,48 @@ +/* + * 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.forward; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.traccar.config.Config; +import org.traccar.config.Keys; + +import java.io.IOException; + +public class EventForwarderAmqp implements EventForwarder { + + private final AmqpClient amqpClient; + private final ObjectMapper objectMapper; + + public EventForwarderAmqp(Config config, ObjectMapper objectMapper) { + String connectionUrl = config.getString(Keys.EVENT_FORWARD_URL); + String exchange = config.getString(Keys.EVENT_FORWARD_EXCHANGE); + String topic = config.getString(Keys.EVENT_FORWARD_TOPIC); + this.objectMapper = objectMapper; + amqpClient = new AmqpClient(connectionUrl, exchange, topic); + } + + @Override + public void forward(EventData eventData, ResultHandler resultHandler) { + try { + String value = objectMapper.writeValueAsString(eventData); + amqpClient.publishMessage(value); + resultHandler.onResult(true, null); + } catch (IOException e) { + resultHandler.onResult(false, e); + } + } +} diff --git a/src/main/java/org/traccar/forward/EventForwarderJson.java b/src/main/java/org/traccar/forward/EventForwarderJson.java new file mode 100644 index 000000000..df53d3d46 --- /dev/null +++ b/src/main/java/org/traccar/forward/EventForwarderJson.java @@ -0,0 +1,68 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.forward; + +import org.traccar.config.Config; +import org.traccar.config.Keys; + +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.InvocationCallback; +import jakarta.ws.rs.core.Response; + +public class EventForwarderJson implements EventForwarder { + + private final String url; + private final String header; + + private final Client client; + + public EventForwarderJson(Config config, Client client) { + this.client = client; + url = config.getString(Keys.EVENT_FORWARD_URL); + header = config.getString(Keys.EVENT_FORWARD_HEADERS); + } + + @Override + public void forward(EventData eventData, ResultHandler resultHandler) { + var requestBuilder = client.target(url).request(); + + if (header != null && !header.isEmpty()) { + for (String line: header.split("\\r?\\n")) { + String[] values = line.split(":", 2); + requestBuilder.header(values[0].trim(), values[1].trim()); + } + } + + requestBuilder.async().post(Entity.json(eventData), new InvocationCallback<Response>() { + @Override + public void completed(Response response) { + if (response.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL) { + resultHandler.onResult(true, null); + } else { + int code = response.getStatusInfo().getStatusCode(); + resultHandler.onResult(false, new RuntimeException("HTTP code " + code)); + } + } + + @Override + public void failed(Throwable throwable) { + resultHandler.onResult(false, throwable); + } + }); + } + +} diff --git a/src/main/java/org/traccar/forward/EventForwarderKafka.java b/src/main/java/org/traccar/forward/EventForwarderKafka.java new file mode 100644 index 000000000..e65c3a51b --- /dev/null +++ b/src/main/java/org/traccar/forward/EventForwarderKafka.java @@ -0,0 +1,58 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.forward; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.Producer; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.traccar.config.Config; +import org.traccar.config.Keys; + +import java.util.Properties; + +public class EventForwarderKafka implements EventForwarder { + + private final Producer<String, String> producer; + private final ObjectMapper objectMapper; + + private final String topic; + + public EventForwarderKafka(Config config, ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + Properties properties = new Properties(); + properties.put("bootstrap.servers", config.getString(Keys.EVENT_FORWARD_URL)); + properties.put("acks", "all"); + properties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer"); + properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer"); + producer = new KafkaProducer<>(properties); + topic = config.getString(Keys.EVENT_FORWARD_TOPIC); + } + + @Override + public void forward(EventData eventData, ResultHandler resultHandler) { + try { + String key = Long.toString(eventData.getDevice().getId()); + String value = objectMapper.writeValueAsString(eventData); + producer.send(new ProducerRecord<>(topic, key, value)); + resultHandler.onResult(true, null); + } catch (JsonProcessingException e) { + resultHandler.onResult(false, e); + } + } + +} diff --git a/src/main/java/org/traccar/forward/EventForwarderMqtt.java b/src/main/java/org/traccar/forward/EventForwarderMqtt.java new file mode 100644 index 000000000..7f4e29384 --- /dev/null +++ b/src/main/java/org/traccar/forward/EventForwarderMqtt.java @@ -0,0 +1,100 @@ +/* + * 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 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; + +public class EventForwarderMqtt implements EventForwarder { + + private final Mqtt5AsyncClient 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.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(); + } 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/NetworkForwarder.java b/src/main/java/org/traccar/forward/NetworkForwarder.java new file mode 100644 index 000000000..86c9a77f3 --- /dev/null +++ b/src/main/java/org/traccar/forward/NetworkForwarder.java @@ -0,0 +1,77 @@ +/* + * 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.forward; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.config.Config; +import org.traccar.config.Keys; + +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.util.HashMap; +import java.util.Map; + +@Singleton +public class NetworkForwarder { + + private static final Logger LOGGER = LoggerFactory.getLogger(NetworkForwarder.class); + + private final InetAddress destination; + private final DatagramSocket connectionUdp; + private final Map<InetSocketAddress, Socket> connectionsTcp = new HashMap<>(); + + @Inject + public NetworkForwarder(Config config) throws IOException { + destination = InetAddress.getByName(config.getString(Keys.SERVER_FORWARD)); + connectionUdp = new DatagramSocket(); + } + + public void forward(InetSocketAddress source, int port, boolean datagram, byte[] data) { + try { + if (datagram) { + connectionUdp.send(new DatagramPacket(data, data.length, destination, port)); + } else { + Socket connectionTcp = connectionsTcp.get(source); + if (connectionTcp == null || connectionTcp.isClosed()) { + connectionTcp = new Socket(destination, port); + connectionsTcp.put(source, connectionTcp); + } + connectionTcp.getOutputStream().write(data); + } + } catch (IOException e) { + LOGGER.warn("Network forwarding error", e); + } + } + + public void disconnect(InetSocketAddress source) { + Socket connectionTcp = connectionsTcp.remove(source); + if (connectionTcp != null) { + try { + connectionTcp.close(); + } catch (IOException e) { + LOGGER.warn("Connection close error", e); + } + } + } + +} diff --git a/src/main/java/org/traccar/forward/PositionData.java b/src/main/java/org/traccar/forward/PositionData.java new file mode 100644 index 000000000..784cf52f5 --- /dev/null +++ b/src/main/java/org/traccar/forward/PositionData.java @@ -0,0 +1,45 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.forward; + +import com.fasterxml.jackson.annotation.JsonInclude; +import org.traccar.model.Device; +import org.traccar.model.Position; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class PositionData { + + private Position position; + + public Position getPosition() { + return position; + } + + public void setPosition(Position position) { + this.position = position; + } + + private Device device; + + public Device getDevice() { + return device; + } + + public void setDevice(Device device) { + this.device = device; + } + +} diff --git a/src/main/java/org/traccar/forward/PositionForwarder.java b/src/main/java/org/traccar/forward/PositionForwarder.java new file mode 100644 index 000000000..58bd1dcc7 --- /dev/null +++ b/src/main/java/org/traccar/forward/PositionForwarder.java @@ -0,0 +1,20 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.forward; + +public interface PositionForwarder { + void forward(PositionData positionData, ResultHandler resultHandler); +} diff --git a/src/main/java/org/traccar/forward/PositionForwarderAmqp.java b/src/main/java/org/traccar/forward/PositionForwarderAmqp.java new file mode 100644 index 000000000..3996bda15 --- /dev/null +++ b/src/main/java/org/traccar/forward/PositionForwarderAmqp.java @@ -0,0 +1,48 @@ +/* + * 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.forward; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.traccar.config.Config; +import org.traccar.config.Keys; + +import java.io.IOException; + +public class PositionForwarderAmqp implements PositionForwarder { + + private final AmqpClient amqpClient; + private final ObjectMapper objectMapper; + + public PositionForwarderAmqp(Config config, ObjectMapper objectMapper) { + String connectionUrl = config.getString(Keys.FORWARD_URL); + String exchange = config.getString(Keys.FORWARD_EXCHANGE); + String topic = config.getString(Keys.FORWARD_TOPIC); + amqpClient = new AmqpClient(connectionUrl, exchange, topic); + this.objectMapper = objectMapper; + } + + @Override + public void forward(PositionData positionData, ResultHandler resultHandler) { + try { + String value = objectMapper.writeValueAsString(positionData); + amqpClient.publishMessage(value); + resultHandler.onResult(true, null); + } catch (IOException e) { + resultHandler.onResult(false, e); + } + } +} diff --git a/src/main/java/org/traccar/forward/PositionForwarderJson.java b/src/main/java/org/traccar/forward/PositionForwarderJson.java new file mode 100644 index 000000000..a0ad8ffd0 --- /dev/null +++ b/src/main/java/org/traccar/forward/PositionForwarderJson.java @@ -0,0 +1,86 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.forward; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.traccar.config.Config; +import org.traccar.config.Keys; + +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.InvocationCallback; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +public class PositionForwarderJson implements PositionForwarder { + + private final String url; + private final String header; + + private final Client client; + private final ObjectMapper objectMapper; + + public PositionForwarderJson(Config config, Client client, ObjectMapper objectMapper) { + this.client = client; + this.objectMapper = objectMapper; + this.url = config.getString(Keys.FORWARD_URL); + this.header = config.getString(Keys.FORWARD_HEADER); + } + + @Override + public void forward(PositionData positionData, ResultHandler resultHandler) { + var requestBuilder = client.target(url).request(); + + MediaType mediaType = MediaType.APPLICATION_JSON_TYPE; + if (header != null && !header.isEmpty()) { + for (String line: header.split("\\r?\\n")) { + String[] values = line.split(":", 2); + String headerName = values[0].trim(); + String headerValue = values[1].trim(); + if (headerName.equals(HttpHeaders.CONTENT_TYPE)) { + mediaType = MediaType.valueOf(headerValue); + } else { + requestBuilder.header(headerName, headerValue); + } + } + } + + try { + var entity = Entity.entity(objectMapper.writeValueAsString(positionData), mediaType); + requestBuilder.async().post(entity, new InvocationCallback<Response>() { + @Override + public void completed(Response response) { + if (response.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL) { + resultHandler.onResult(true, null); + } else { + int code = response.getStatusInfo().getStatusCode(); + resultHandler.onResult(false, new RuntimeException("HTTP code " + code)); + } + } + + @Override + public void failed(Throwable throwable) { + resultHandler.onResult(false, throwable); + } + }); + } catch (JsonProcessingException e) { + resultHandler.onResult(false, e); + } + } + +} diff --git a/src/main/java/org/traccar/forward/PositionForwarderKafka.java b/src/main/java/org/traccar/forward/PositionForwarderKafka.java new file mode 100644 index 000000000..7432e9364 --- /dev/null +++ b/src/main/java/org/traccar/forward/PositionForwarderKafka.java @@ -0,0 +1,58 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.forward; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.Producer; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.traccar.config.Config; +import org.traccar.config.Keys; + +import java.util.Properties; + +public class PositionForwarderKafka implements PositionForwarder { + + private final Producer<String, String> producer; + private final ObjectMapper objectMapper; + + private final String topic; + + public PositionForwarderKafka(Config config, ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + Properties properties = new Properties(); + properties.put("bootstrap.servers", config.getString(Keys.FORWARD_URL)); + properties.put("acks", "all"); + properties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer"); + properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer"); + producer = new KafkaProducer<>(properties); + topic = config.getString(Keys.FORWARD_TOPIC); + } + + @Override + public void forward(PositionData positionData, ResultHandler resultHandler) { + try { + String key = Long.toString(positionData.getDevice().getId()); + String value = objectMapper.writeValueAsString(positionData); + producer.send(new ProducerRecord<>(topic, key, value)); + resultHandler.onResult(true, null); + } catch (JsonProcessingException e) { + resultHandler.onResult(false, e); + } + } + +} diff --git a/src/main/java/org/traccar/forward/PositionForwarderRedis.java b/src/main/java/org/traccar/forward/PositionForwarderRedis.java new file mode 100644 index 000000000..539d247b6 --- /dev/null +++ b/src/main/java/org/traccar/forward/PositionForwarderRedis.java @@ -0,0 +1,50 @@ +/* + * 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.forward; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.traccar.config.Config; +import org.traccar.config.Keys; +import redis.clients.jedis.Jedis; + +public class PositionForwarderRedis implements PositionForwarder { + + private final String url; + + private final ObjectMapper objectMapper; + + public PositionForwarderRedis(Config config, ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + this.url = config.getString(Keys.FORWARD_URL); + } + + @Override + public void forward(PositionData positionData, ResultHandler resultHandler) { + + try { + String key = "positions." + positionData.getDevice().getUniqueId(); + String value = objectMapper.writeValueAsString(positionData.getPosition()); + try (Jedis jedis = new Jedis(url)) { + jedis.lpush(key, value); + } + resultHandler.onResult(true, null); + } catch (JsonProcessingException e) { + resultHandler.onResult(false, e); + } + } + +} diff --git a/src/main/java/org/traccar/forward/PositionForwarderUrl.java b/src/main/java/org/traccar/forward/PositionForwarderUrl.java new file mode 100644 index 000000000..33474d40b --- /dev/null +++ b/src/main/java/org/traccar/forward/PositionForwarderUrl.java @@ -0,0 +1,166 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.forward; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.helper.Checksum; +import org.traccar.model.Device; +import org.traccar.model.Position; + +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.InvocationCallback; +import jakarta.ws.rs.core.Response; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Calendar; +import java.util.Formatter; +import java.util.Locale; +import java.util.TimeZone; + +public class PositionForwarderUrl implements PositionForwarder { + + private final String url; + private final String header; + + private final Client client; + private final ObjectMapper objectMapper; + + public PositionForwarderUrl(Config config, Client client, ObjectMapper objectMapper) { + this.client = client; + this.objectMapper = objectMapper; + this.url = config.getString(Keys.FORWARD_URL); + this.header = config.getString(Keys.FORWARD_HEADER); + } + + @Override + public void forward(PositionData positionData, ResultHandler resultHandler) { + try { + String url = formatRequest(positionData); + var requestBuilder = client.target(url).request(); + + if (header != null && !header.isEmpty()) { + for (String line: header.split("\\r?\\n")) { + String[] values = line.split(":", 2); + String headerName = values[0].trim(); + String headerValue = values[1].trim(); + requestBuilder.header(headerName, headerValue); + } + } + + requestBuilder.async().get(new InvocationCallback<Response>() { + @Override + public void completed(Response response) { + if (response.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL) { + resultHandler.onResult(true, null); + } else { + int code = response.getStatusInfo().getStatusCode(); + resultHandler.onResult(false, new RuntimeException("HTTP code " + code)); + } + } + + @Override + public void failed(Throwable throwable) { + resultHandler.onResult(false, throwable); + } + }); + } catch (UnsupportedEncodingException | JsonProcessingException e) { + resultHandler.onResult(false, e); + } + } + + public String formatRequest( + PositionData positionData) throws UnsupportedEncodingException, JsonProcessingException { + + Position position = positionData.getPosition(); + Device device = positionData.getDevice(); + + String request = url + .replace("{name}", URLEncoder.encode(device.getName(), StandardCharsets.UTF_8)) + .replace("{uniqueId}", device.getUniqueId()) + .replace("{status}", device.getStatus()) + .replace("{deviceId}", String.valueOf(position.getDeviceId())) + .replace("{protocol}", String.valueOf(position.getProtocol())) + .replace("{deviceTime}", String.valueOf(position.getDeviceTime().getTime())) + .replace("{fixTime}", String.valueOf(position.getFixTime().getTime())) + .replace("{valid}", String.valueOf(position.getValid())) + .replace("{latitude}", String.valueOf(position.getLatitude())) + .replace("{longitude}", String.valueOf(position.getLongitude())) + .replace("{altitude}", String.valueOf(position.getAltitude())) + .replace("{speed}", String.valueOf(position.getSpeed())) + .replace("{course}", String.valueOf(position.getCourse())) + .replace("{accuracy}", String.valueOf(position.getAccuracy())) + .replace("{statusCode}", calculateStatus(position)); + + if (position.getAddress() != null) { + request = request.replace( + "{address}", URLEncoder.encode(position.getAddress(), StandardCharsets.UTF_8)); + } + + if (request.contains("{attributes}")) { + String attributes = objectMapper.writeValueAsString(position.getAttributes()); + request = request.replace( + "{attributes}", URLEncoder.encode(attributes, StandardCharsets.UTF_8)); + } + + if (request.contains("{gprmc}")) { + request = request.replace("{gprmc}", formatSentence(position)); + } + + return request; + } + + private static String formatSentence(Position position) { + + StringBuilder s = new StringBuilder("$GPRMC,"); + + try (Formatter f = new Formatter(s, Locale.ENGLISH)) { + + Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"), Locale.ENGLISH); + calendar.setTimeInMillis(position.getFixTime().getTime()); + + f.format("%1$tH%1$tM%1$tS.%1$tL,A,", calendar); + + double lat = position.getLatitude(); + double lon = position.getLongitude(); + + f.format("%02d%07.4f,%c,", (int) Math.abs(lat), Math.abs(lat) % 1 * 60, lat < 0 ? 'S' : 'N'); + f.format("%03d%07.4f,%c,", (int) Math.abs(lon), Math.abs(lon) % 1 * 60, lon < 0 ? 'W' : 'E'); + + f.format("%.2f,%.2f,", position.getSpeed(), position.getCourse()); + f.format("%1$td%1$tm%1$ty,,", calendar); + } + + s.append(Checksum.nmea(s.substring(1))); + + return s.toString(); + } + + // OpenGTS status code + private String calculateStatus(Position position) { + if (position.hasAttribute(Position.KEY_ALARM)) { + return "0xF841"; // STATUS_PANIC_ON + } else if (position.getSpeed() < 1.0) { + return "0xF020"; // STATUS_LOCATION + } else { + return "0xF11C"; // STATUS_MOTION_MOVING + } + } + +} diff --git a/src/main/java/org/traccar/forward/ResultHandler.java b/src/main/java/org/traccar/forward/ResultHandler.java new file mode 100644 index 000000000..009daf495 --- /dev/null +++ b/src/main/java/org/traccar/forward/ResultHandler.java @@ -0,0 +1,20 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.forward; + +public interface ResultHandler { + void onResult(boolean success, Throwable throwable); +} diff --git a/src/main/java/org/traccar/geocoder/BanGeocoder.java b/src/main/java/org/traccar/geocoder/BanGeocoder.java index b1f0900a4..e2ff72311 100644 --- a/src/main/java/org/traccar/geocoder/BanGeocoder.java +++ b/src/main/java/org/traccar/geocoder/BanGeocoder.java @@ -20,13 +20,14 @@ package org.traccar.geocoder; * API documentation: https://adresse.data.gouv.fr/api */ -import javax.json.JsonArray; -import javax.json.JsonObject; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class BanGeocoder extends JsonGeocoder { - public BanGeocoder(int cacheSize, AddressFormat addressFormat) { - super("https://api-adresse.data.gouv.fr/reverse/?lat=%f&lon=%f", cacheSize, addressFormat); + public BanGeocoder(Client client, int cacheSize, AddressFormat addressFormat) { + super(client, "https://api-adresse.data.gouv.fr/reverse/?lat=%f&lon=%f", cacheSize, addressFormat); } @Override diff --git a/src/main/java/org/traccar/geocoder/BingMapsGeocoder.java b/src/main/java/org/traccar/geocoder/BingMapsGeocoder.java index 32a26ee0c..bc3b15ce7 100644 --- a/src/main/java/org/traccar/geocoder/BingMapsGeocoder.java +++ b/src/main/java/org/traccar/geocoder/BingMapsGeocoder.java @@ -16,13 +16,14 @@ */ package org.traccar.geocoder; -import javax.json.JsonArray; -import javax.json.JsonObject; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class BingMapsGeocoder extends JsonGeocoder { - public BingMapsGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) { - super(url + "/Locations/%f,%f?key=" + key + "&include=ciso2", cacheSize, addressFormat); + public BingMapsGeocoder(Client client, String url, String key, int cacheSize, AddressFormat addressFormat) { + super(client, url + "/Locations/%f,%f?key=" + key + "&include=ciso2", cacheSize, addressFormat); } @Override diff --git a/src/main/java/org/traccar/geocoder/FactualGeocoder.java b/src/main/java/org/traccar/geocoder/FactualGeocoder.java index f540eb8fe..6c8891316 100644 --- a/src/main/java/org/traccar/geocoder/FactualGeocoder.java +++ b/src/main/java/org/traccar/geocoder/FactualGeocoder.java @@ -16,7 +16,8 @@ */ package org.traccar.geocoder; -import javax.json.JsonObject; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class FactualGeocoder extends JsonGeocoder { @@ -28,8 +29,8 @@ public class FactualGeocoder extends JsonGeocoder { return url; } - public FactualGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) { - super(formatUrl(url, key), cacheSize, addressFormat); + public FactualGeocoder(Client client, String url, String key, int cacheSize, AddressFormat addressFormat) { + super(client, formatUrl(url, key), cacheSize, addressFormat); } @Override diff --git a/src/main/java/org/traccar/geocoder/GeoapifyGeocoder.java b/src/main/java/org/traccar/geocoder/GeoapifyGeocoder.java index ef0e4c8bd..35a47bb88 100644 --- a/src/main/java/org/traccar/geocoder/GeoapifyGeocoder.java +++ b/src/main/java/org/traccar/geocoder/GeoapifyGeocoder.java @@ -15,8 +15,9 @@ */ package org.traccar.geocoder; -import javax.json.JsonArray; -import javax.json.JsonObject; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class GeoapifyGeocoder extends JsonGeocoder { @@ -31,8 +32,8 @@ public class GeoapifyGeocoder extends JsonGeocoder { return url; } - public GeoapifyGeocoder(String key, String language, int cacheSize, AddressFormat addressFormat) { - super(formatUrl(key, language), cacheSize, addressFormat); + public GeoapifyGeocoder(Client client, String key, String language, int cacheSize, AddressFormat addressFormat) { + super(client, formatUrl(key, language), cacheSize, addressFormat); } @Override diff --git a/src/main/java/org/traccar/geocoder/GeocodeFarmGeocoder.java b/src/main/java/org/traccar/geocoder/GeocodeFarmGeocoder.java index 39a3300a0..80b00b3cc 100644 --- a/src/main/java/org/traccar/geocoder/GeocodeFarmGeocoder.java +++ b/src/main/java/org/traccar/geocoder/GeocodeFarmGeocoder.java @@ -15,7 +15,8 @@ */ package org.traccar.geocoder; -import javax.json.JsonObject; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class GeocodeFarmGeocoder extends JsonGeocoder { @@ -30,8 +31,9 @@ public class GeocodeFarmGeocoder extends JsonGeocoder { } return url; } - public GeocodeFarmGeocoder(String key, String language, int cacheSize, AddressFormat addressFormat) { - super(formatUrl(key, language), cacheSize, addressFormat); + public GeocodeFarmGeocoder( + Client client, String key, String language, int cacheSize, AddressFormat addressFormat) { + super(client, formatUrl(key, language), cacheSize, addressFormat); } @Override diff --git a/src/main/java/org/traccar/geocoder/GeocodeXyzGeocoder.java b/src/main/java/org/traccar/geocoder/GeocodeXyzGeocoder.java index aca360c3d..e88962e1a 100644 --- a/src/main/java/org/traccar/geocoder/GeocodeXyzGeocoder.java +++ b/src/main/java/org/traccar/geocoder/GeocodeXyzGeocoder.java @@ -15,7 +15,8 @@ */ package org.traccar.geocoder; -import javax.json.JsonObject; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class GeocodeXyzGeocoder extends JsonGeocoder { @@ -27,8 +28,8 @@ public class GeocodeXyzGeocoder extends JsonGeocoder { return url; } - public GeocodeXyzGeocoder(String key, int cacheSize, AddressFormat addressFormat) { - super(formatUrl(key), cacheSize, addressFormat); + public GeocodeXyzGeocoder(Client client, String key, int cacheSize, AddressFormat addressFormat) { + super(client, formatUrl(key), cacheSize, addressFormat); } @Override diff --git a/src/main/java/org/traccar/geocoder/Geocoder.java b/src/main/java/org/traccar/geocoder/Geocoder.java index 587a27520..f4abe871a 100644 --- a/src/main/java/org/traccar/geocoder/Geocoder.java +++ b/src/main/java/org/traccar/geocoder/Geocoder.java @@ -15,6 +15,8 @@ */ package org.traccar.geocoder; +import org.traccar.database.StatisticsManager; + public interface Geocoder { interface ReverseGeocoderCallback { @@ -27,4 +29,6 @@ public interface Geocoder { String getAddress(double latitude, double longitude, ReverseGeocoderCallback callback); + void setStatisticsManager(StatisticsManager statisticsManager); + } diff --git a/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java b/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java index b4881a006..062e795eb 100644 --- a/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java +++ b/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java @@ -15,7 +15,8 @@ */ package org.traccar.geocoder; -import javax.json.JsonObject; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class GisgraphyGeocoder extends JsonGeocoder { @@ -27,8 +28,8 @@ public class GisgraphyGeocoder extends JsonGeocoder { return url; } - public GisgraphyGeocoder(String url, int cacheSize, AddressFormat addressFormat) { - super(formatUrl(url), cacheSize, addressFormat); + public GisgraphyGeocoder(Client client, String url, int cacheSize, AddressFormat addressFormat) { + super(client, formatUrl(url), cacheSize, addressFormat); } @Override diff --git a/src/main/java/org/traccar/geocoder/GoogleGeocoder.java b/src/main/java/org/traccar/geocoder/GoogleGeocoder.java index 9494cab45..93f128b46 100644 --- a/src/main/java/org/traccar/geocoder/GoogleGeocoder.java +++ b/src/main/java/org/traccar/geocoder/GoogleGeocoder.java @@ -15,9 +15,10 @@ */ package org.traccar.geocoder; -import javax.json.JsonArray; -import javax.json.JsonObject; -import javax.json.JsonString; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.json.JsonString; +import jakarta.ws.rs.client.Client; public class GoogleGeocoder extends JsonGeocoder { @@ -32,8 +33,8 @@ public class GoogleGeocoder extends JsonGeocoder { return url; } - public GoogleGeocoder(String key, String language, int cacheSize, AddressFormat addressFormat) { - super(formatUrl(key, language), cacheSize, addressFormat); + public GoogleGeocoder(Client client, String key, String language, int cacheSize, AddressFormat addressFormat) { + super(client, formatUrl(key, language), cacheSize, addressFormat); } @Override diff --git a/src/main/java/org/traccar/geocoder/HereGeocoder.java b/src/main/java/org/traccar/geocoder/HereGeocoder.java index 40390e65b..2d1bc1bf4 100644 --- a/src/main/java/org/traccar/geocoder/HereGeocoder.java +++ b/src/main/java/org/traccar/geocoder/HereGeocoder.java @@ -15,7 +15,8 @@ */ package org.traccar.geocoder; -import javax.json.JsonObject; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class HereGeocoder extends JsonGeocoder { @@ -35,8 +36,9 @@ public class HereGeocoder extends JsonGeocoder { } public HereGeocoder( - String url, String id, String key, String language, int cacheSize, AddressFormat addressFormat) { - super(formatUrl(url, id, key, language), cacheSize, addressFormat); + Client client, String url, String id, String key, String language, + int cacheSize, AddressFormat addressFormat) { + super(client, formatUrl(url, id, key, language), cacheSize, addressFormat); } @Override diff --git a/src/main/java/org/traccar/geocoder/JsonGeocoder.java b/src/main/java/org/traccar/geocoder/JsonGeocoder.java index f20aa79d6..f9b039f43 100644 --- a/src/main/java/org/traccar/geocoder/JsonGeocoder.java +++ b/src/main/java/org/traccar/geocoder/JsonGeocoder.java @@ -17,14 +17,12 @@ package org.traccar.geocoder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.traccar.Context; -import org.traccar.Main; import org.traccar.database.StatisticsManager; -import javax.json.JsonObject; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.client.Invocation; -import javax.ws.rs.client.InvocationCallback; +import jakarta.json.JsonObject; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.InvocationCallback; import java.util.AbstractMap; import java.util.Collections; import java.util.LinkedHashMap; @@ -34,16 +32,19 @@ public abstract class JsonGeocoder implements Geocoder { private static final Logger LOGGER = LoggerFactory.getLogger(JsonGeocoder.class); + private final Client client; private final String url; private final AddressFormat addressFormat; + private StatisticsManager statisticsManager; private Map<Map.Entry<Double, Double>, String> cache; - public JsonGeocoder(String url, final int cacheSize, AddressFormat addressFormat) { + public JsonGeocoder(Client client, String url, final int cacheSize, AddressFormat addressFormat) { + this.client = client; this.url = url; this.addressFormat = addressFormat; if (cacheSize > 0) { - this.cache = Collections.synchronizedMap(new LinkedHashMap<Map.Entry<Double, Double>, String>() { + this.cache = Collections.synchronizedMap(new LinkedHashMap<>() { @Override protected boolean removeEldestEntry(Map.Entry eldest) { return size() > cacheSize; @@ -52,6 +53,11 @@ public abstract class JsonGeocoder implements Geocoder { } } + @Override + public void setStatisticsManager(StatisticsManager statisticsManager) { + this.statisticsManager = statisticsManager; + } + protected String readValue(JsonObject object, String key) { if (object.containsKey(key) && !object.isNull(key)) { return object.getString(key); @@ -97,11 +103,11 @@ public abstract class JsonGeocoder implements Geocoder { } } - if (Main.getInjector() != null) { - Main.getInjector().getInstance(StatisticsManager.class).registerGeocoderRequest(); + if (statisticsManager != null) { + statisticsManager.registerGeocoderRequest(); } - Invocation.Builder request = Context.getClient().target(String.format(url, latitude, longitude)).request(); + var request = client.target(String.format(url, latitude, longitude)).request(); if (callback != null) { request.async().get(new InvocationCallback<JsonObject>() { diff --git a/src/main/java/org/traccar/DeviceSession.java b/src/main/java/org/traccar/geocoder/LocationIqGeocoder.java index 322381807..f304ffeff 100644 --- a/src/main/java/org/traccar/DeviceSession.java +++ b/src/main/java/org/traccar/geocoder/LocationIqGeocoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,30 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.traccar; +package org.traccar.geocoder; -import java.util.TimeZone; +import jakarta.ws.rs.client.Client; -public class DeviceSession { +public class LocationIqGeocoder extends NominatimGeocoder { - private final long deviceId; + private static final String DEFAULT_URL = "https://us1.locationiq.com/v1/reverse.php"; - public DeviceSession(long deviceId) { - this.deviceId = deviceId; - } - - public long getDeviceId() { - return deviceId; - } - - private TimeZone timeZone; - - public void setTimeZone(TimeZone timeZone) { - this.timeZone = timeZone; - } - - public TimeZone getTimeZone() { - return timeZone; + public LocationIqGeocoder( + Client client, String url, String key, String language, int cacheSize, AddressFormat addressFormat) { + super(client, url != null ? url : DEFAULT_URL, key, language, cacheSize, addressFormat); } } diff --git a/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java b/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java index 8dc3f76f0..1b6c8adcc 100644 --- a/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java +++ b/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java @@ -16,8 +16,9 @@ */ package org.traccar.geocoder; -import javax.json.JsonArray; -import javax.json.JsonObject; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class MapQuestGeocoder extends JsonGeocoder { @@ -29,8 +30,8 @@ public class MapQuestGeocoder extends JsonGeocoder { return url; } - public MapQuestGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) { - super(formatUrl(url, key), cacheSize, addressFormat); + public MapQuestGeocoder(Client client, String url, String key, int cacheSize, AddressFormat addressFormat) { + super(client, formatUrl(url, key), cacheSize, addressFormat); } @Override diff --git a/src/main/java/org/traccar/geocoder/MapTilerGeocoder.java b/src/main/java/org/traccar/geocoder/MapTilerGeocoder.java index 6b688a6e8..24c9da2ad 100644 --- a/src/main/java/org/traccar/geocoder/MapTilerGeocoder.java +++ b/src/main/java/org/traccar/geocoder/MapTilerGeocoder.java @@ -15,13 +15,14 @@ */ package org.traccar.geocoder; -import javax.json.JsonArray; -import javax.json.JsonObject; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class MapTilerGeocoder extends JsonGeocoder { - public MapTilerGeocoder(String key, int cacheSize, AddressFormat addressFormat) { - super("https://api.maptiler.com/geocoding/%2$f,%1$f.json?key=" + key, cacheSize, addressFormat); + public MapTilerGeocoder(Client client, String key, int cacheSize, AddressFormat addressFormat) { + super(client, "https://api.maptiler.com/geocoding/%2$f,%1$f.json?key=" + key, cacheSize, addressFormat); } @Override diff --git a/src/main/java/org/traccar/geocoder/MapboxGeocoder.java b/src/main/java/org/traccar/geocoder/MapboxGeocoder.java index 9b987c9d8..9fa6b8d88 100644 --- a/src/main/java/org/traccar/geocoder/MapboxGeocoder.java +++ b/src/main/java/org/traccar/geocoder/MapboxGeocoder.java @@ -15,9 +15,10 @@ */ package org.traccar.geocoder; -import javax.json.JsonArray; -import javax.json.JsonObject; -import javax.json.JsonString; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.json.JsonString; +import jakarta.ws.rs.client.Client; public class MapboxGeocoder extends JsonGeocoder { @@ -25,8 +26,8 @@ public class MapboxGeocoder extends JsonGeocoder { return "https://api.mapbox.com/geocoding/v5/mapbox.places/%2$f,%1$f.json?access_token=" + key; } - public MapboxGeocoder(String key, int cacheSize, AddressFormat addressFormat) { - super(formatUrl(key), cacheSize, addressFormat); + public MapboxGeocoder(Client client, String key, int cacheSize, AddressFormat addressFormat) { + super(client, formatUrl(key), cacheSize, addressFormat); } @Override diff --git a/src/main/java/org/traccar/geocoder/MapmyIndiaGeocoder.java b/src/main/java/org/traccar/geocoder/MapmyIndiaGeocoder.java index 2b70708a1..b68db07bc 100644 --- a/src/main/java/org/traccar/geocoder/MapmyIndiaGeocoder.java +++ b/src/main/java/org/traccar/geocoder/MapmyIndiaGeocoder.java @@ -15,13 +15,14 @@ */ package org.traccar.geocoder; -import javax.json.JsonArray; -import javax.json.JsonObject; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class MapmyIndiaGeocoder extends JsonGeocoder { - public MapmyIndiaGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) { - super(url + "/" + key + "/rev_geocode?lat=%f&lng=%f", cacheSize, addressFormat); + public MapmyIndiaGeocoder(Client client, String url, String key, int cacheSize, AddressFormat addressFormat) { + super(client, url + "/" + key + "/rev_geocode?lat=%f&lng=%f", cacheSize, addressFormat); } @Override diff --git a/src/main/java/org/traccar/geocoder/NominatimGeocoder.java b/src/main/java/org/traccar/geocoder/NominatimGeocoder.java index 8db25bf15..1e26d0042 100644 --- a/src/main/java/org/traccar/geocoder/NominatimGeocoder.java +++ b/src/main/java/org/traccar/geocoder/NominatimGeocoder.java @@ -15,7 +15,8 @@ */ package org.traccar.geocoder; -import javax.json.JsonObject; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class NominatimGeocoder extends JsonGeocoder { @@ -33,8 +34,9 @@ public class NominatimGeocoder extends JsonGeocoder { return url; } - public NominatimGeocoder(String url, String key, String language, int cacheSize, AddressFormat addressFormat) { - super(formatUrl(url, key, language), cacheSize, addressFormat); + public NominatimGeocoder( + Client client, String url, String key, String language, int cacheSize, AddressFormat addressFormat) { + super(client, formatUrl(url, key, language), cacheSize, addressFormat); } @Override diff --git a/src/main/java/org/traccar/geocoder/OpenCageGeocoder.java b/src/main/java/org/traccar/geocoder/OpenCageGeocoder.java index bbcc00cd0..4607fdc87 100644 --- a/src/main/java/org/traccar/geocoder/OpenCageGeocoder.java +++ b/src/main/java/org/traccar/geocoder/OpenCageGeocoder.java @@ -16,8 +16,9 @@ */ package org.traccar.geocoder; -import javax.json.JsonArray; -import javax.json.JsonObject; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class OpenCageGeocoder extends JsonGeocoder { @@ -32,8 +33,9 @@ public class OpenCageGeocoder extends JsonGeocoder { return url; } - public OpenCageGeocoder(String url, String key, String language, int cacheSize, AddressFormat addressFormat) { - super(formatUrl(url, key, language), cacheSize, addressFormat); + public OpenCageGeocoder( + Client client, String url, String key, String language, int cacheSize, AddressFormat addressFormat) { + super(client, formatUrl(url, key, language), cacheSize, addressFormat); } @Override diff --git a/src/main/java/org/traccar/geocoder/PositionStackGeocoder.java b/src/main/java/org/traccar/geocoder/PositionStackGeocoder.java index 2674a68ca..4aed27fc5 100644 --- a/src/main/java/org/traccar/geocoder/PositionStackGeocoder.java +++ b/src/main/java/org/traccar/geocoder/PositionStackGeocoder.java @@ -15,8 +15,9 @@ */ package org.traccar.geocoder; -import javax.json.JsonArray; -import javax.json.JsonObject; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class PositionStackGeocoder extends JsonGeocoder { @@ -24,8 +25,8 @@ public class PositionStackGeocoder extends JsonGeocoder { return "http://api.positionstack.com/v1/reverse?access_key=" + key + "&query=%f,%f"; } - public PositionStackGeocoder(String key, int cacheSize, AddressFormat addressFormat) { - super(formatUrl(key), cacheSize, addressFormat); + public PositionStackGeocoder(Client client, String key, int cacheSize, AddressFormat addressFormat) { + super(client, formatUrl(key), cacheSize, addressFormat); } @Override diff --git a/src/main/java/org/traccar/geocoder/TestGeocoder.java b/src/main/java/org/traccar/geocoder/TestGeocoder.java new file mode 100644 index 000000000..259f13c6c --- /dev/null +++ b/src/main/java/org/traccar/geocoder/TestGeocoder.java @@ -0,0 +1,36 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.geocoder; + +import org.traccar.database.StatisticsManager; + +public class TestGeocoder implements Geocoder { + + @Override + public void setStatisticsManager(StatisticsManager statisticsManager) { + } + + @Override + public String getAddress(double latitude, double longitude, ReverseGeocoderCallback callback) { + String address = String.format("Location %f, %f", latitude, longitude); + if (callback != null) { + callback.onSuccess(address); + return null; + } + return address; + } + +} diff --git a/src/main/java/org/traccar/geocoder/TomTomGeocoder.java b/src/main/java/org/traccar/geocoder/TomTomGeocoder.java index 232b24396..4d452fd43 100644 --- a/src/main/java/org/traccar/geocoder/TomTomGeocoder.java +++ b/src/main/java/org/traccar/geocoder/TomTomGeocoder.java @@ -15,8 +15,9 @@ */ package org.traccar.geocoder; -import javax.json.JsonArray; -import javax.json.JsonObject; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class TomTomGeocoder extends JsonGeocoder { @@ -28,8 +29,8 @@ public class TomTomGeocoder extends JsonGeocoder { return url; } - public TomTomGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) { - super(formatUrl(url, key), cacheSize, addressFormat); + public TomTomGeocoder(Client client, String url, String key, int cacheSize, AddressFormat addressFormat) { + super(client, formatUrl(url, key), cacheSize, addressFormat); } @Override diff --git a/src/main/java/org/traccar/geofence/GeofenceCircle.java b/src/main/java/org/traccar/geofence/GeofenceCircle.java index 5d566f84e..59feb1730 100644 --- a/src/main/java/org/traccar/geofence/GeofenceCircle.java +++ b/src/main/java/org/traccar/geofence/GeofenceCircle.java @@ -18,7 +18,9 @@ package org.traccar.geofence; import java.text.DecimalFormat; import java.text.ParseException; +import org.traccar.config.Config; import org.traccar.helper.DistanceCalculator; +import org.traccar.model.Geofence; public class GeofenceCircle extends GeofenceGeometry { @@ -44,7 +46,7 @@ public class GeofenceCircle extends GeofenceGeometry { } @Override - public boolean containsPoint(double latitude, double longitude) { + public boolean containsPoint(Config config, Geofence geofence, double latitude, double longitude) { return distanceFromCenter(latitude, longitude) <= radius; } diff --git a/src/main/java/org/traccar/geofence/GeofenceGeometry.java b/src/main/java/org/traccar/geofence/GeofenceGeometry.java index 2c45c22af..fadabab1c 100644 --- a/src/main/java/org/traccar/geofence/GeofenceGeometry.java +++ b/src/main/java/org/traccar/geofence/GeofenceGeometry.java @@ -15,11 +15,14 @@ */ package org.traccar.geofence; +import org.traccar.config.Config; +import org.traccar.model.Geofence; + import java.text.ParseException; public abstract class GeofenceGeometry { - public abstract boolean containsPoint(double latitude, double longitude); + public abstract boolean containsPoint(Config config, Geofence geofence, double latitude, double longitude); public abstract double calculateArea(); diff --git a/src/main/java/org/traccar/geofence/GeofencePolygon.java b/src/main/java/org/traccar/geofence/GeofencePolygon.java index cd2cbf16a..13f6658ef 100644 --- a/src/main/java/org/traccar/geofence/GeofencePolygon.java +++ b/src/main/java/org/traccar/geofence/GeofencePolygon.java @@ -19,6 +19,8 @@ import org.locationtech.spatial4j.context.SpatialContext; import org.locationtech.spatial4j.context.jts.JtsSpatialContextFactory; import org.locationtech.spatial4j.shape.ShapeFactory; import org.locationtech.spatial4j.shape.jts.JtsShapeFactory; +import org.traccar.config.Config; +import org.traccar.model.Geofence; import java.text.ParseException; import java.util.ArrayList; @@ -95,7 +97,7 @@ public class GeofencePolygon extends GeofenceGeometry { } @Override - public boolean containsPoint(double latitude, double longitude) { + public boolean containsPoint(Config config, Geofence geofence, double latitude, double longitude) { int polyCorners = coordinates.size(); int i; diff --git a/src/main/java/org/traccar/geofence/GeofencePolyline.java b/src/main/java/org/traccar/geofence/GeofencePolyline.java index 370bf99bb..d9c280ae4 100644 --- a/src/main/java/org/traccar/geofence/GeofencePolyline.java +++ b/src/main/java/org/traccar/geofence/GeofencePolyline.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,23 +19,28 @@ package org.traccar.geofence; import java.text.ParseException; import java.util.ArrayList; +import org.traccar.config.Config; +import org.traccar.config.Keys; import org.traccar.helper.DistanceCalculator; +import org.traccar.model.Geofence; public class GeofencePolyline extends GeofenceGeometry { private ArrayList<Coordinate> coordinates; - private double distance; public GeofencePolyline() { } - public GeofencePolyline(String wkt, double distance) throws ParseException { + public GeofencePolyline(String wkt) throws ParseException { fromWkt(wkt); - this.distance = distance; } @Override - public boolean containsPoint(double latitude, double longitude) { + public boolean containsPoint(Config config, Geofence geofence, double latitude, double longitude) { + double distance = geofence.getDouble("polylineDistance"); + if (distance == 0) { + distance = config.getDouble(Keys.GEOFENCE_POLYLINE_DISTANCE); + } for (int i = 1; i < coordinates.size(); i++) { if (DistanceCalculator.distanceToLine( latitude, longitude, coordinates.get(i - 1).getLat(), coordinates.get(i - 1).getLon(), @@ -56,9 +61,9 @@ public class GeofencePolyline extends GeofenceGeometry { StringBuilder buf = new StringBuilder(); buf.append("LINESTRING ("); for (Coordinate coordinate : coordinates) { - buf.append(String.valueOf(coordinate.getLat())); + buf.append(coordinate.getLat()); buf.append(" "); - buf.append(String.valueOf(coordinate.getLon())); + buf.append(coordinate.getLon()); buf.append(", "); } return buf.substring(0, buf.length() - 2) + ")"; @@ -105,8 +110,4 @@ public class GeofencePolyline extends GeofenceGeometry { } - public void setDistance(double distance) { - this.distance = distance; - } - } diff --git a/src/main/java/org/traccar/geolocation/GoogleGeolocationProvider.java b/src/main/java/org/traccar/geolocation/GoogleGeolocationProvider.java index 5901b47cd..9425e9111 100644 --- a/src/main/java/org/traccar/geolocation/GoogleGeolocationProvider.java +++ b/src/main/java/org/traccar/geolocation/GoogleGeolocationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,12 +15,14 @@ */ package org.traccar.geolocation; +import jakarta.ws.rs.client.Client; + public class GoogleGeolocationProvider extends UniversalGeolocationProvider { private static final String URL = "https://www.googleapis.com/geolocation/v1/geolocate"; - public GoogleGeolocationProvider(String key) { - super(URL, key); + public GoogleGeolocationProvider(Client client, String key) { + super(client, URL, key); } } diff --git a/src/main/java/org/traccar/geolocation/MozillaGeolocationProvider.java b/src/main/java/org/traccar/geolocation/MozillaGeolocationProvider.java index c6a73a52b..7eb22dcca 100644 --- a/src/main/java/org/traccar/geolocation/MozillaGeolocationProvider.java +++ b/src/main/java/org/traccar/geolocation/MozillaGeolocationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org) + * 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. @@ -15,12 +15,14 @@ */ 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(String key) { - super(URL, key != null ? key : "test"); + public MozillaGeolocationProvider(Client client, String key) { + super(client, URL, key != null ? key : "test"); } } diff --git a/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java b/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java index 2535970d3..72a05d10f 100644 --- a/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java +++ b/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org) + * 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. @@ -15,18 +15,20 @@ */ package org.traccar.geolocation; -import org.traccar.Context; import org.traccar.model.CellTower; import org.traccar.model.Network; -import javax.json.JsonObject; -import javax.ws.rs.client.InvocationCallback; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.InvocationCallback; public class OpenCellIdGeolocationProvider implements GeolocationProvider { - private String url; + private final Client client; + private final String url; - public OpenCellIdGeolocationProvider(String url, String key) { + public OpenCellIdGeolocationProvider(Client client, String url, String key) { + this.client = client; if (url == null) { url = "http://opencellid.org/cell/get"; } @@ -41,7 +43,7 @@ public class OpenCellIdGeolocationProvider implements GeolocationProvider { String request = String.format(url, cellTower.getMobileCountryCode(), cellTower.getMobileNetworkCode(), cellTower.getLocationAreaCode(), cellTower.getCellId()); - Context.getClient().target(request).request().async().get(new InvocationCallback<JsonObject>() { + client.target(request).request().async().get(new InvocationCallback<JsonObject>() { @Override public void completed(JsonObject json) { if (json.containsKey("lat") && json.containsKey("lon")) { diff --git a/src/main/java/org/traccar/geolocation/UniversalGeolocationProvider.java b/src/main/java/org/traccar/geolocation/UniversalGeolocationProvider.java index 33cd84a47..9086d8ce3 100644 --- a/src/main/java/org/traccar/geolocation/UniversalGeolocationProvider.java +++ b/src/main/java/org/traccar/geolocation/UniversalGeolocationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,26 +15,26 @@ */ package org.traccar.geolocation; -import org.traccar.Context; import org.traccar.model.Network; -import javax.json.JsonObject; -import javax.ws.rs.client.AsyncInvoker; -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.InvocationCallback; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.InvocationCallback; public class UniversalGeolocationProvider implements GeolocationProvider { + private final Client client; private final String url; - public UniversalGeolocationProvider(String url, String key) { + public UniversalGeolocationProvider(Client client, String url, String key) { + this.client = client; this.url = url + "?key=" + key; } @Override public void getLocation(Network network, final LocationProviderCallback callback) { - AsyncInvoker invoker = Context.getClient().target(url).request().async(); - invoker.post(Entity.json(network), new InvocationCallback<JsonObject>() { + client.target(url).request().async().post(Entity.json(network), new InvocationCallback<JsonObject>() { @Override public void completed(JsonObject json) { if (json.containsKey("error")) { diff --git a/src/main/java/org/traccar/geolocation/UnwiredGeolocationProvider.java b/src/main/java/org/traccar/geolocation/UnwiredGeolocationProvider.java index 963bcb688..4f1c5617e 100644 --- a/src/main/java/org/traccar/geolocation/UnwiredGeolocationProvider.java +++ b/src/main/java/org/traccar/geolocation/UnwiredGeolocationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 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. @@ -19,22 +19,23 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; -import org.traccar.Context; import org.traccar.model.CellTower; import org.traccar.model.Network; import org.traccar.model.WifiAccessPoint; -import javax.json.JsonObject; -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.InvocationCallback; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.InvocationCallback; import java.util.Collection; public class UnwiredGeolocationProvider implements GeolocationProvider { - private String url; - private String key; + private final Client client; + private final String url; + private final String key; - private ObjectMapper objectMapper; + private final ObjectMapper objectMapper; private abstract static class NetworkMixIn { @JsonProperty("mcc") @@ -73,7 +74,8 @@ public class UnwiredGeolocationProvider implements GeolocationProvider { abstract Integer getSignalStrength(); } - public UnwiredGeolocationProvider(String url, String key) { + public UnwiredGeolocationProvider(Client client, String url, String key) { + this.client = client; this.url = url; this.key = key; @@ -88,7 +90,7 @@ public class UnwiredGeolocationProvider implements GeolocationProvider { ObjectNode json = objectMapper.valueToTree(network); json.put("token", key); - Context.getClient().target(url).request().async().post(Entity.json(json), new InvocationCallback<JsonObject>() { + client.target(url).request().async().post(Entity.json(json), new InvocationCallback<JsonObject>() { @Override public void completed(JsonObject json) { if (json.getString("status").equals("error")) { diff --git a/src/main/java/org/traccar/handler/AcknowledgementHandler.java b/src/main/java/org/traccar/handler/AcknowledgementHandler.java new file mode 100644 index 000000000..4c1085998 --- /dev/null +++ b/src/main/java/org/traccar/handler/AcknowledgementHandler.java @@ -0,0 +1,121 @@ +/* + * 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.handler; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.ChannelPromise; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +public class AcknowledgementHandler extends ChannelOutboundHandlerAdapter { + + private static final Logger LOGGER = LoggerFactory.getLogger(AcknowledgementHandler.class); + + public interface Event { + } + + public static class EventReceived implements Event { + } + + public static class EventDecoded implements Event { + private final Collection<Object> objects; + + public EventDecoded(Collection<Object> objects) { + this.objects = objects; + } + + public Collection<Object> getObjects() { + return objects; + } + } + + public static class EventHandled implements Event { + private final Object object; + + public EventHandled(Object object) { + this.object = object; + } + + public Object getObject() { + return object; + } + } + + private static final class Entry { + private final Object message; + private final ChannelPromise promise; + + private Entry(Object message, ChannelPromise promise) { + this.message = message; + this.promise = promise; + } + + public Object getMessage() { + return message; + } + + public ChannelPromise getPromise() { + return promise; + } + } + + private List<Entry> queue; + private final Set<Object> waiting = new HashSet<>(); + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + List<Entry> output = new LinkedList<>(); + synchronized (this) { + if (msg instanceof Event) { + if (msg instanceof EventReceived) { + LOGGER.debug("Event received"); + if (queue == null) { + queue = new LinkedList<>(); + } + } else if (msg instanceof EventDecoded) { + EventDecoded event = (EventDecoded) msg; + LOGGER.debug("Event decoded {}", event.getObjects().size()); + waiting.addAll(event.getObjects()); + } else if (msg instanceof EventHandled) { + EventHandled event = (EventHandled) msg; + LOGGER.debug("Event handled"); + waiting.remove(event.getObject()); + } + if (!(msg instanceof EventReceived) && waiting.isEmpty()) { + output.addAll(queue); + queue = null; + } + } else if (queue != null) { + LOGGER.debug("Message queued"); + queue.add(new Entry(msg, promise)); + } else { + LOGGER.debug("Message sent"); + output.add(new Entry(msg, promise)); + } + } + for (Entry entry : output) { + ctx.write(entry.getMessage(), entry.getPromise()); + } + } + +} diff --git a/src/main/java/org/traccar/handler/ComputedAttributesHandler.java b/src/main/java/org/traccar/handler/ComputedAttributesHandler.java index 153da29b9..042747359 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 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,56 +24,77 @@ 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.jexl2.JexlEngine; -import org.apache.commons.jexl2.JexlException; -import org.apache.commons.jexl2.MapContext; +import org.apache.commons.jexl3.JexlFeatures; +import org.apache.commons.jexl3.JexlEngine; +import org.apache.commons.jexl3.JexlBuilder; +import org.apache.commons.jexl3.introspection.JexlSandbox; +import org.apache.commons.jexl3.JexlException; +import org.apache.commons.jexl3.MapContext; 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.database.AttributesManager; -import org.traccar.database.IdentityManager; import org.traccar.model.Attribute; import org.traccar.model.Device; import org.traccar.model.Position; +import org.traccar.session.cache.CacheManager; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; + +@Singleton @ChannelHandler.Sharable public class ComputedAttributesHandler extends BaseDataHandler { private static final Logger LOGGER = LoggerFactory.getLogger(ComputedAttributesHandler.class); - private final IdentityManager identityManager; - private final AttributesManager attributesManager; + private final CacheManager cacheManager; private final JexlEngine engine; + private final JexlFeatures features; + private final boolean includeDeviceAttributes; - public ComputedAttributesHandler( - Config config, IdentityManager identityManager, AttributesManager attributesManager) { - this.identityManager = identityManager; - this.attributesManager = attributesManager; - engine = new JexlEngine(); - engine.setStrict(true); - engine.setFunctions(Collections.singletonMap("math", Math.class)); + @Inject + public ComputedAttributesHandler(Config config, CacheManager cacheManager) { + this.cacheManager = cacheManager; + JexlSandbox sandbox = new JexlSandbox(false); + sandbox.allow("com.safe.Functions"); + sandbox.allow(Math.class.getName()); + List.of( + Double.class, Float.class, Integer.class, Long.class, Short.class, + Character.class, Boolean.class, String.class, Byte.class) + .forEach((type) -> sandbox.allow(type.getName())); + features = new JexlFeatures() + .localVar(config.getBoolean(Keys.PROCESSING_COMPUTED_ATTRIBUTES_LOCAL_VARIABLES)) + .loops(config.getBoolean(Keys.PROCESSING_COMPUTED_ATTRIBUTES_LOOPS)) + .newInstance(config.getBoolean(Keys.PROCESSING_COMPUTED_ATTRIBUTES_NEW_INSTANCE_CREATION)) + .structuredLiteral(true); + engine = new JexlBuilder() + .strict(true) + .namespaces(Collections.singletonMap("math", Math.class)) + .sandbox(sandbox) + .create(); includeDeviceAttributes = config.getBoolean(Keys.PROCESSING_COMPUTED_ATTRIBUTES_DEVICE_ATTRIBUTES); } private MapContext prepareContext(Position position) { MapContext result = new MapContext(); if (includeDeviceAttributes) { - Device device = identityManager.getById(position.getDeviceId()); + Device device = cacheManager.getObject(Device.class, position.getDeviceId()); if (device != null) { - for (Object key : device.getAttributes().keySet()) { - result.set((String) key, device.getAttributes().get(key)); + for (String key : device.getAttributes().keySet()) { + result.set(key, device.getAttributes().get(key)); } } } Set<Method> methods = new HashSet<>(Arrays.asList(position.getClass().getMethods())); - methods.removeAll(Arrays.asList(Object.class.getMethods())); + Arrays.asList(Object.class.getMethods()).forEach(methods::remove); for (Method method : methods) { if (method.getName().startsWith("get") && method.getParameterTypes().length == 0) { String name = Character.toLowerCase(method.getName().charAt(3)) + method.getName().substring(4); @@ -82,8 +103,8 @@ public class ComputedAttributesHandler extends BaseDataHandler { if (!method.getReturnType().equals(Map.class)) { result.set(name, method.invoke(position)); } else { - for (Object key : ((Map) method.invoke(position)).keySet()) { - result.set((String) key, ((Map) method.invoke(position)).get(key)); + for (Object key : ((Map<?, ?>) method.invoke(position)).keySet()) { + result.set((String) key, ((Map<?, ?>) method.invoke(position)).get(key)); } } } catch (IllegalAccessException | InvocationTargetException error) { @@ -99,13 +120,14 @@ public class ComputedAttributesHandler extends BaseDataHandler { */ @Deprecated public Object computeAttribute(Attribute attribute, Position position) throws JexlException { - return engine.createExpression(attribute.getExpression()).evaluate(prepareContext(position)); + return engine + .createScript(features, engine.createInfo(), attribute.getExpression()) + .execute(prepareContext(position)); } @Override protected Position handlePosition(Position position) { - Collection<Attribute> attributes = attributesManager.getItems( - attributesManager.getAllDeviceItems(position.getDeviceId())); + Collection<Attribute> attributes = cacheManager.getDeviceObjects(position.getDeviceId(), Attribute.class); for (Attribute attribute : attributes) { if (attribute.getAttribute() != null) { Object result = null; @@ -116,17 +138,45 @@ public class ComputedAttributesHandler extends BaseDataHandler { } if (result != null) { try { - switch (attribute.getType()) { - case "number": - Number numberValue = (Number) result; - position.getAttributes().put(attribute.getAttribute(), numberValue); + switch (attribute.getAttribute()) { + case "valid": + position.setValid((Boolean) result); + break; + case "latitude": + position.setLatitude(((Number) result).doubleValue()); + break; + case "longitude": + position.setLongitude(((Number) result).doubleValue()); + break; + case "altitude": + position.setAltitude(((Number) result).doubleValue()); break; - case "boolean": - Boolean booleanValue = (Boolean) result; - position.getAttributes().put(attribute.getAttribute(), booleanValue); + case "speed": + position.setSpeed(((Number) result).doubleValue()); + break; + case "course": + position.setCourse(((Number) result).doubleValue()); + break; + case "address": + position.setAddress((String) result); + break; + case "accuracy": + position.setAccuracy(((Number) result).doubleValue()); break; default: - position.getAttributes().put(attribute.getAttribute(), result.toString()); + switch (attribute.getType()) { + case "number": + Number numberValue = (Number) result; + position.getAttributes().put(attribute.getAttribute(), numberValue); + break; + case "boolean": + Boolean booleanValue = (Boolean) result; + position.getAttributes().put(attribute.getAttribute(), booleanValue); + break; + default: + position.getAttributes().put(attribute.getAttribute(), result.toString()); + } + break; } } catch (ClassCastException error) { LOGGER.warn("Attribute cast error", error); diff --git a/src/main/java/org/traccar/handler/CopyAttributesHandler.java b/src/main/java/org/traccar/handler/CopyAttributesHandler.java index f386116b0..42b438e41 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 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) * Copyright 2016 - 2017 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,27 +18,39 @@ package org.traccar.handler; import io.netty.channel.ChannelHandler; import org.traccar.BaseDataHandler; -import org.traccar.database.IdentityManager; +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 { - private IdentityManager identityManager; + private final boolean enabled; + private final CacheManager cacheManager; - public CopyAttributesHandler(IdentityManager identityManager) { - this.identityManager = identityManager; + @Inject + public CopyAttributesHandler(Config config, CacheManager cacheManager) { + enabled = config.getBoolean(Keys.PROCESSING_COPY_ATTRIBUTES_ENABLE); + this.cacheManager = cacheManager; } @Override protected Position handlePosition(Position position) { - String attributesString = identityManager.lookupAttributeString( - position.getDeviceId(), "processing.copyAttributes", "", false, true); - Position last = identityManager.getLastPosition(position.getDeviceId()); - if (last != null) { - for (String attribute : attributesString.split("[ ,]")) { - if (last.getAttributes().containsKey(attribute) && !position.getAttributes().containsKey(attribute)) { - position.getAttributes().put(attribute, last.getAttributes().get(attribute)); + 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)); + } } } } diff --git a/src/main/java/org/traccar/handler/DefaultDataHandler.java b/src/main/java/org/traccar/handler/DefaultDataHandler.java index 9d8ea044d..cca6dcd0a 100644 --- a/src/main/java/org/traccar/handler/DefaultDataHandler.java +++ b/src/main/java/org/traccar/handler/DefaultDataHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org) + * 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. @@ -19,25 +19,32 @@ import io.netty.channel.ChannelHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.BaseDataHandler; -import org.traccar.database.DataManager; 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 { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultDataHandler.class); - private final DataManager dataManager; + private final Storage storage; - public DefaultDataHandler(DataManager dataManager) { - this.dataManager = dataManager; + @Inject + public DefaultDataHandler(Storage storage) { + this.storage = storage; } @Override protected Position handlePosition(Position position) { try { - dataManager.addObject(position); + position.setId(storage.addObject(position, new Request(new Columns.Exclude("id")))); } catch (Exception error) { LOGGER.warn("Failed to store position", error); } diff --git a/src/main/java/org/traccar/handler/DistanceHandler.java b/src/main/java/org/traccar/handler/DistanceHandler.java index 1e7e444f6..7fdefa1f4 100644 --- a/src/main/java/org/traccar/handler/DistanceHandler.java +++ b/src/main/java/org/traccar/handler/DistanceHandler.java @@ -1,6 +1,6 @@ /* + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) * Copyright 2015 Amila Silva - * Copyright 2016 - 2021 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,24 +20,28 @@ import io.netty.channel.ChannelHandler; import org.traccar.BaseDataHandler; import org.traccar.config.Config; import org.traccar.config.Keys; -import org.traccar.database.IdentityManager; import org.traccar.helper.DistanceCalculator; import org.traccar.model.Position; +import org.traccar.session.cache.CacheManager; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import java.math.BigDecimal; import java.math.RoundingMode; +@Singleton @ChannelHandler.Sharable public class DistanceHandler extends BaseDataHandler { - private final IdentityManager identityManager; + private final CacheManager cacheManager; private final boolean filter; private final int coordinatesMinError; private final int coordinatesMaxError; - public DistanceHandler(Config config, IdentityManager identityManager) { - this.identityManager = identityManager; + @Inject + public DistanceHandler(Config config, CacheManager cacheManager) { + this.cacheManager = cacheManager; this.filter = config.getBoolean(Keys.COORDINATES_FILTER); this.coordinatesMinError = config.getInteger(Keys.COORDINATES_MIN_ERROR); this.coordinatesMaxError = config.getInteger(Keys.COORDINATES_MAX_ERROR); @@ -47,15 +51,15 @@ public class DistanceHandler extends BaseDataHandler { protected Position handlePosition(Position position) { double distance = 0.0; - if (position.getAttributes().containsKey(Position.KEY_DISTANCE)) { + if (position.hasAttribute(Position.KEY_DISTANCE)) { distance = position.getDouble(Position.KEY_DISTANCE); } double totalDistance = 0.0; - Position last = identityManager != null ? identityManager.getLastPosition(position.getDeviceId()) : null; + Position last = cacheManager.getPosition(position.getDeviceId()); if (last != null) { totalDistance = last.getDouble(Position.KEY_TOTAL_DISTANCE); - if (!position.getAttributes().containsKey(Position.KEY_DISTANCE)) { + if (!position.hasAttribute(Position.KEY_DISTANCE)) { distance = DistanceCalculator.distance( position.getLatitude(), position.getLongitude(), last.getLatitude(), last.getLongitude()); diff --git a/src/main/java/org/traccar/handler/EngineHoursHandler.java b/src/main/java/org/traccar/handler/EngineHoursHandler.java index 92da84e6b..621205b34 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 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org) * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,22 +18,27 @@ package org.traccar.handler; import io.netty.channel.ChannelHandler; import org.traccar.BaseDataHandler; -import org.traccar.database.IdentityManager; 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 { - private final IdentityManager identityManager; + private final CacheManager cacheManager; - public EngineHoursHandler(IdentityManager identityManager) { - this.identityManager = identityManager; + @Inject + public EngineHoursHandler(CacheManager cacheManager) { + this.cacheManager = cacheManager; } @Override protected Position handlePosition(Position position) { - if (!position.getAttributes().containsKey(Position.KEY_HOURS)) { - Position last = identityManager.getLastPosition(position.getDeviceId()); + if (!position.hasAttribute(Position.KEY_HOURS)) { + Position last = cacheManager.getPosition(position.getDeviceId()); if (last != null) { long hours = last.getLong(Position.KEY_HOURS); if (last.getBoolean(Position.KEY_IGNITION) && position.getBoolean(Position.KEY_IGNITION)) { diff --git a/src/main/java/org/traccar/handler/FilterHandler.java b/src/main/java/org/traccar/handler/FilterHandler.java index e576a26b8..a15d3ffad 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 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2014 - 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. @@ -16,51 +16,90 @@ 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.BaseDataHandler; -import org.traccar.Context; import org.traccar.config.Config; import org.traccar.config.Keys; +import org.traccar.database.StatisticsManager; import org.traccar.helper.UnitsConverter; +import org.traccar.helper.model.AttributeUtil; +import org.traccar.model.Calendar; +import org.traccar.model.Device; import org.traccar.model.Position; +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.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 BaseDataHandler { +public class FilterHandler extends ChannelInboundHandlerAdapter { private static final Logger LOGGER = LoggerFactory.getLogger(FilterHandler.class); - private boolean filterInvalid; - private boolean filterZero; - private boolean filterDuplicate; - private long filterFuture; - private boolean filterApproximate; - private int filterAccuracy; - private boolean filterStatic; - private int filterDistance; - private int filterMaxSpeed; - private long filterMinPeriod; - private boolean filterRelative; - private long skipLimit; - private boolean skipAttributes; - - public FilterHandler(Config config) { + private final boolean enabled; + private final boolean filterInvalid; + private final boolean filterZero; + private final boolean filterDuplicate; + private final boolean filterOutdated; + private final long filterFuture; + private final long filterPast; + private final boolean filterApproximate; + private final int filterAccuracy; + private final boolean filterStatic; + private final int filterDistance; + private final int filterMaxSpeed; + private final long filterMinPeriod; + private final int filterDailyLimit; + private final boolean filterRelative; + private final long skipLimit; + private final boolean skipAttributes; + + private final CacheManager cacheManager; + private final Storage storage; + private final StatisticsManager statisticsManager; + + @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); + filterOutdated = config.getBoolean(Keys.FILTER_OUTDATED); filterFuture = config.getLong(Keys.FILTER_FUTURE) * 1000; + filterPast = config.getLong(Keys.FILTER_PAST) * 1000; filterAccuracy = config.getInteger(Keys.FILTER_ACCURACY); filterApproximate = config.getBoolean(Keys.FILTER_APPROXIMATE); filterStatic = config.getBoolean(Keys.FILTER_STATIC); filterDistance = config.getInteger(Keys.FILTER_DISTANCE); filterMaxSpeed = config.getInteger(Keys.FILTER_MAX_SPEED); - filterMinPeriod = config.getInteger(Keys.FILTER_MIN_PERIOD) * 1000; + filterMinPeriod = config.getInteger(Keys.FILTER_MIN_PERIOD) * 1000L; + filterDailyLimit = config.getInteger(Keys.FILTER_DAILY_LIMIT); filterRelative = config.getBoolean(Keys.FILTER_RELATIVE); skipLimit = config.getLong(Keys.FILTER_SKIP_LIMIT) * 1000; skipAttributes = config.getBoolean(Keys.FILTER_SKIP_ATTRIBUTES_ENABLE); + this.cacheManager = cacheManager; + this.storage = storage; + this.statisticsManager = statisticsManager; + } + + private Position getPrecedingPosition(long deviceId, Date date) throws StorageException { + return storage.getObject(Position.class, new Request( + new Columns.All(), + new Condition.And( + new Condition.Equals("deviceId", deviceId), + new Condition.Compare("fixTime", "<=", "time", date)), + new Order("fixTime", true, 1))); } private boolean filterInvalid(Position position) { @@ -76,7 +115,7 @@ public class FilterHandler extends BaseDataHandler { private boolean filterDuplicate(Position position, Position last) { if (filterDuplicate && last != null && position.getFixTime().equals(last.getFixTime())) { for (String key : position.getAttributes().keySet()) { - if (!last.getAttributes().containsKey(key)) { + if (!last.hasAttribute(key)) { return false; } } @@ -85,10 +124,18 @@ public class FilterHandler extends BaseDataHandler { return false; } + private boolean filterOutdated(Position position) { + return filterOutdated && position.getOutdated(); + } + private boolean filterFuture(Position position) { return filterFuture != 0 && position.getFixTime().getTime() > System.currentTimeMillis() + filterFuture; } + private boolean filterPast(Position position) { + return filterPast != 0 && position.getFixTime().getTime() < System.currentTimeMillis() - filterPast; + } + private boolean filterAccuracy(Position position) { return filterAccuracy != 0 && position.getAccuracy() > filterAccuracy; } @@ -125,6 +172,13 @@ public class FilterHandler extends BaseDataHandler { return false; } + private boolean filterDailyLimit(Position position) { + if (filterDailyLimit != 0) { + return statisticsManager.messageStoredCount(position.getDeviceId()) >= filterDailyLimit; + } + return false; + } + private boolean skipLimit(Position position, Position last) { if (skipLimit != 0 && last != null) { return (position.getServerTime().getTime() - last.getServerTime().getTime()) > skipLimit; @@ -134,10 +188,9 @@ public class FilterHandler extends BaseDataHandler { private boolean skipAttributes(Position position) { if (skipAttributes) { - String attributesString = Context.getIdentityManager().lookupAttributeString( - position.getDeviceId(), "filter.skipAttributes", "", false, true); - for (String attribute : attributesString.split("[ ,]")) { - if (position.getAttributes().containsKey(attribute)) { + String string = AttributeUtil.lookup(cacheManager, Keys.FILTER_SKIP_ATTRIBUTES, position.getDeviceId()); + for (String attribute : string.split("[ ,]")) { + if (position.hasAttribute(attribute)) { return true; } } @@ -145,7 +198,7 @@ public class FilterHandler extends BaseDataHandler { return false; } - private boolean filter(Position position) { + protected boolean filter(Position position) { StringBuilder filterType = new StringBuilder(); @@ -156,15 +209,24 @@ public class FilterHandler extends BaseDataHandler { if (filterZero(position)) { filterType.append("Zero "); } + if (filterOutdated(position)) { + filterType.append("Outdated "); + } if (filterFuture(position)) { filterType.append("Future "); } + if (filterPast(position)) { + filterType.append("Past "); + } if (filterAccuracy(position)) { filterType.append("Accuracy "); } if (filterApproximate(position)) { filterType.append("Approximate "); } + if (filterDailyLimit(position)) { + filterType.append("DailyLimit "); + } // filter out excessive data long deviceId = position.getDeviceId(); @@ -173,13 +235,13 @@ public class FilterHandler extends BaseDataHandler { if (filterRelative) { try { Date newFixTime = position.getFixTime(); - preceding = Context.getDataManager().getPrecedingPosition(deviceId, newFixTime); + preceding = getPrecedingPosition(deviceId, newFixTime); } catch (StorageException e) { LOGGER.warn("Error retrieving preceding position; fallbacking to last received position.", e); - preceding = getLastReceivedPosition(deviceId); + preceding = cacheManager.getPosition(deviceId); } } else { - preceding = getLastReceivedPosition(deviceId); + preceding = cacheManager.getPosition(deviceId); } if (filterDuplicate(position, preceding) && !skipLimit(position, preceding) && !skipAttributes(position)) { filterType.append("Duplicate "); @@ -198,34 +260,34 @@ public class FilterHandler extends BaseDataHandler { } } - if (filterType.length() > 0) { - - StringBuilder message = new StringBuilder(); - message.append("Position filtered by "); - message.append(filterType.toString()); - message.append("filters from device: "); - message.append(Context.getIdentityManager().getById(deviceId).getUniqueId()); + Device device = cacheManager.getObject(Device.class, deviceId); + if (device.getCalendarId() > 0) { + Calendar calendar = cacheManager.getObject(Calendar.class, device.getCalendarId()); + if (!calendar.checkMoment(position.getFixTime())) { + filterType.append("Calendar "); + } + } - LOGGER.info(message.toString()); + if (filterType.length() > 0) { + LOGGER.info("Position filtered by {}filters from device: {}", filterType, device.getUniqueId()); return true; } return false; } - private Position getLastReceivedPosition(long deviceId) { - if (Context.getIdentityManager() != null) { - return Context.getIdentityManager().getLastPosition(deviceId); - } - return null; - } - @Override - protected Position handlePosition(Position position) { - if (filter(position)) { - return null; + 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); + } + } else { + super.channelRead(ctx, msg); } - return position; } } diff --git a/src/main/java/org/traccar/handler/GeocoderHandler.java b/src/main/java/org/traccar/handler/GeocoderHandler.java index 614cf97d6..e4f240a90 100644 --- a/src/main/java/org/traccar/handler/GeocoderHandler.java +++ b/src/main/java/org/traccar/handler/GeocoderHandler.java @@ -20,12 +20,11 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.traccar.Context; import org.traccar.config.Config; import org.traccar.config.Keys; -import org.traccar.database.IdentityManager; import org.traccar.geocoder.Geocoder; import org.traccar.model.Position; +import org.traccar.session.cache.CacheManager; @ChannelHandler.Sharable public class GeocoderHandler extends ChannelInboundHandlerAdapter { @@ -33,18 +32,17 @@ public class GeocoderHandler extends ChannelInboundHandlerAdapter { private static final Logger LOGGER = LoggerFactory.getLogger(GeocoderHandler.class); private final Geocoder geocoder; - private final IdentityManager identityManager; + private final CacheManager cacheManager; private final boolean ignorePositions; private final boolean processInvalidPositions; - private final int geocoderReuseDistance; + private final int reuseDistance; - public GeocoderHandler( - Config config, Geocoder geocoder, IdentityManager identityManager) { + public GeocoderHandler(Config config, Geocoder geocoder, CacheManager cacheManager) { this.geocoder = geocoder; - this.identityManager = identityManager; - ignorePositions = Context.getConfig().getBoolean(Keys.GEOCODER_IGNORE_POSITIONS); + this.cacheManager = cacheManager; + ignorePositions = config.getBoolean(Keys.GEOCODER_IGNORE_POSITIONS); processInvalidPositions = config.getBoolean(Keys.GEOCODER_PROCESS_INVALID_POSITIONS); - geocoderReuseDistance = config.getInteger(Keys.GEOCODER_REUSE_DISTANCE, 0); + reuseDistance = config.getInteger(Keys.GEOCODER_REUSE_DISTANCE, 0); } @Override @@ -52,10 +50,10 @@ public class GeocoderHandler extends ChannelInboundHandlerAdapter { if (message instanceof Position && !ignorePositions) { final Position position = (Position) message; if (processInvalidPositions || position.getValid()) { - if (geocoderReuseDistance != 0) { - Position lastPosition = identityManager.getLastPosition(position.getDeviceId()); + if (reuseDistance != 0) { + Position lastPosition = cacheManager.getPosition(position.getDeviceId()); if (lastPosition != null && lastPosition.getAddress() != null - && position.getDouble(Position.KEY_DISTANCE) <= geocoderReuseDistance) { + && position.getDouble(Position.KEY_DISTANCE) <= reuseDistance) { position.setAddress(lastPosition.getAddress()); ctx.fireChannelRead(position); return; diff --git a/src/main/java/org/traccar/handler/GeofenceHandler.java b/src/main/java/org/traccar/handler/GeofenceHandler.java new file mode 100644 index 000000000..68bc6dbf0 --- /dev/null +++ b/src/main/java/org/traccar/handler/GeofenceHandler.java @@ -0,0 +1,52 @@ +/* + * 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.handler; + +import io.netty.channel.ChannelHandler; +import org.traccar.BaseDataHandler; +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 { + + private final Config config; + private final CacheManager cacheManager; + + @Inject + public GeofenceHandler(Config config, CacheManager cacheManager) { + this.config = config; + this.cacheManager = cacheManager; + } + + @Override + protected Position handlePosition(Position position) { + + List<Long> geofenceIds = GeofenceUtil.getCurrentGeofences(config, cacheManager, position); + if (!geofenceIds.isEmpty()) { + position.setGeofenceIds(geofenceIds); + } + return position; + } + +} diff --git a/src/main/java/org/traccar/handler/GeolocationHandler.java b/src/main/java/org/traccar/handler/GeolocationHandler.java index 0e78322c8..e7389f22d 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 - 2018 Anton Tananaev (anton@traccar.org) + * 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. @@ -25,6 +25,7 @@ import org.traccar.config.Keys; import org.traccar.database.StatisticsManager; import org.traccar.geolocation.GeolocationProvider; import org.traccar.model.Position; +import org.traccar.session.cache.CacheManager; @ChannelHandler.Sharable public class GeolocationHandler extends ChannelInboundHandlerAdapter { @@ -32,14 +33,19 @@ public class GeolocationHandler extends ChannelInboundHandlerAdapter { private static final Logger LOGGER = LoggerFactory.getLogger(GeolocationHandler.class); private final GeolocationProvider geolocationProvider; + private final CacheManager cacheManager; private final StatisticsManager statisticsManager; private final boolean processInvalidPositions; + private final boolean reuse; public GeolocationHandler( - Config config, GeolocationProvider geolocationProvider, StatisticsManager statisticsManager) { + Config config, GeolocationProvider geolocationProvider, CacheManager cacheManager, + StatisticsManager statisticsManager) { this.geolocationProvider = geolocationProvider; + this.cacheManager = cacheManager; this.statisticsManager = statisticsManager; - this.processInvalidPositions = config.getBoolean(Keys.GEOLOCATION_PROCESS_INVALID_POSITIONS); + processInvalidPositions = config.getBoolean(Keys.GEOLOCATION_PROCESS_INVALID_POSITIONS); + reuse = config.getBoolean(Keys.GEOLOCATION_REUSE); } @Override @@ -48,6 +54,17 @@ public class GeolocationHandler extends ChannelInboundHandlerAdapter { final Position position = (Position) message; if ((position.getOutdated() || processInvalidPositions && !position.getValid()) && position.getNetwork() != 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; + } + } + if (statisticsManager != null) { statisticsManager.registerGeolocationRequest(); } @@ -56,15 +73,7 @@ public class GeolocationHandler extends ChannelInboundHandlerAdapter { new GeolocationProvider.LocationProviderCallback() { @Override public void onSuccess(double latitude, double longitude, double accuracy) { - position.set(Position.KEY_APPROXIMATE, true); - position.setValid(true); - position.setFixTime(position.getDeviceTime()); - position.setLatitude(latitude); - position.setLongitude(longitude); - position.setAccuracy(accuracy); - position.setAltitude(0); - position.setSpeed(0); - position.setCourse(0); + updatePosition(position, latitude, longitude, accuracy); ctx.fireChannelRead(position); } @@ -82,4 +91,16 @@ public class GeolocationHandler extends ChannelInboundHandlerAdapter { } } + private void updatePosition(Position position, double latitude, double longitude, double accuracy) { + position.set(Position.KEY_APPROXIMATE, true); + position.setValid(true); + position.setFixTime(position.getDeviceTime()); + position.setLatitude(latitude); + position.setLongitude(longitude); + position.setAccuracy(accuracy); + position.setAltitude(0); + position.setSpeed(0); + position.setCourse(0); + } + } diff --git a/src/main/java/org/traccar/handler/HemisphereHandler.java b/src/main/java/org/traccar/handler/HemisphereHandler.java index aff3d8a64..294e449db 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 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,12 +21,17 @@ 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 { private int latitudeFactor; private int longitudeFactor; + @Inject public HemisphereHandler(Config config) { String latitudeHemisphere = config.getString(Keys.LOCATION_LATITUDE_HEMISPHERE); if (latitudeHemisphere != null) { @@ -36,7 +41,7 @@ public class HemisphereHandler extends BaseDataHandler { latitudeFactor = -1; } } - String longitudeHemisphere = config.getString(Keys.LOCATION_LATITUDE_HEMISPHERE); + String longitudeHemisphere = config.getString(Keys.LOCATION_LONGITUDE_HEMISPHERE); if (longitudeHemisphere != null) { if (longitudeHemisphere.equalsIgnoreCase("E")) { longitudeFactor = 1; diff --git a/src/main/java/org/traccar/handler/MotionHandler.java b/src/main/java/org/traccar/handler/MotionHandler.java index e8051dd75..68a31a16a 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 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,21 +18,31 @@ package org.traccar.handler; import io.netty.channel.ChannelHandler; import org.traccar.BaseDataHandler; +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 { - private double speedThreshold; + private final CacheManager cacheManager; - public MotionHandler(double speedThreshold) { - this.speedThreshold = speedThreshold; + @Inject + public MotionHandler(CacheManager cacheManager) { + this.cacheManager = cacheManager; } @Override protected Position handlePosition(Position position) { - if (!position.getAttributes().containsKey(Position.KEY_MOTION)) { - position.set(Position.KEY_MOTION, position.getSpeed() > speedThreshold); + 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; } diff --git a/src/main/java/org/traccar/handler/NetworkForwarderHandler.java b/src/main/java/org/traccar/handler/NetworkForwarderHandler.java new file mode 100644 index 000000000..470e175ca --- /dev/null +++ b/src/main/java/org/traccar/handler/NetworkForwarderHandler.java @@ -0,0 +1,72 @@ +/* + * 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.handler; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.socket.DatagramChannel; +import io.netty.channel.socket.DatagramPacket; +import org.traccar.forward.NetworkForwarder; + +import jakarta.inject.Inject; +import java.net.InetSocketAddress; +import java.net.SocketAddress; + +public class NetworkForwarderHandler extends ChannelInboundHandlerAdapter { + + private final int port; + + private NetworkForwarder networkForwarder; + + public NetworkForwarderHandler(int port) { + this.port = port; + } + + @Inject + public void setNetworkForwarder(NetworkForwarder networkForwarder) { + this.networkForwarder = networkForwarder; + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + boolean datagram = ctx.channel() instanceof DatagramChannel; + SocketAddress remoteAddress; + ByteBuf buffer; + if (datagram) { + DatagramPacket message = (DatagramPacket) msg; + remoteAddress = message.recipient(); + buffer = message.content(); + } else { + remoteAddress = ctx.channel().remoteAddress(); + buffer = (ByteBuf) msg; + } + + byte[] data = new byte[buffer.readableBytes()]; + buffer.getBytes(buffer.readerIndex(), data); + networkForwarder.forward((InetSocketAddress) remoteAddress, port, datagram, data); + super.channelRead(ctx, msg); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + if (!(ctx.channel() instanceof DatagramChannel)) { + networkForwarder.disconnect((InetSocketAddress) ctx.channel().remoteAddress()); + } + super.channelInactive(ctx); + } + +} diff --git a/src/main/java/org/traccar/handler/RemoteAddressHandler.java b/src/main/java/org/traccar/handler/RemoteAddressHandler.java index c09b8c39a..61ada5b91 100644 --- a/src/main/java/org/traccar/handler/RemoteAddressHandler.java +++ b/src/main/java/org/traccar/handler/RemoteAddressHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org) + * 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. @@ -18,22 +18,36 @@ package org.traccar.handler; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; +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 { + private final boolean enabled; + + @Inject + public RemoteAddressHandler(Config config) { + enabled = config.getBoolean(Keys.PROCESSING_REMOTE_ADDRESS_ENABLE); + } + @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { - InetSocketAddress remoteAddress = (InetSocketAddress) ctx.channel().remoteAddress(); - String hostAddress = remoteAddress != null ? remoteAddress.getAddress().getHostAddress() : null; + if (enabled) { + InetSocketAddress remoteAddress = (InetSocketAddress) ctx.channel().remoteAddress(); + String hostAddress = remoteAddress != null ? remoteAddress.getAddress().getHostAddress() : null; - if (msg instanceof Position) { - Position position = (Position) msg; - position.set(Position.KEY_IP, hostAddress); + if (msg instanceof Position) { + Position position = (Position) msg; + position.set(Position.KEY_IP, hostAddress); + } } ctx.fireChannelRead(msg); diff --git a/src/main/java/org/traccar/handler/SpeedLimitHandler.java b/src/main/java/org/traccar/handler/SpeedLimitHandler.java index 65f2c9cfe..6edb6e912 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 Anton Tananaev (anton@traccar.org) + * Copyright 2020 - 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. @@ -23,6 +23,10 @@ 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 { @@ -30,6 +34,7 @@ public class SpeedLimitHandler extends ChannelInboundHandlerAdapter { private final SpeedLimitProvider speedLimitProvider; + @Inject public SpeedLimitHandler(SpeedLimitProvider speedLimitProvider) { this.speedLimitProvider = speedLimitProvider; } diff --git a/src/main/java/org/traccar/handler/StandardLoggingHandler.java b/src/main/java/org/traccar/handler/StandardLoggingHandler.java index 13c5f8281..84492e2a5 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 Anton Tananaev (anton@traccar.org) + * Copyright 2019 - 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. @@ -23,6 +23,7 @@ import io.netty.channel.ChannelPromise; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.NetworkMessage; +import org.traccar.helper.NetworkUtil; import java.net.InetSocketAddress; import java.net.SocketAddress; @@ -63,7 +64,7 @@ public class StandardLoggingHandler extends ChannelDuplexHandler { public void log(ChannelHandlerContext ctx, boolean downstream, SocketAddress remoteAddress, ByteBuf buf) { StringBuilder message = new StringBuilder(); - message.append("[").append(ctx.channel().id().asShortText()).append(": "); + message.append("[").append(NetworkUtil.session(ctx.channel())).append(": "); message.append(protocol); if (downstream) { message.append(" > "); @@ -76,9 +77,8 @@ public class StandardLoggingHandler extends ChannelDuplexHandler { } else { message.append("unknown"); } - message.append("]"); + message.append("] "); - message.append(" HEX: "); message.append(ByteBufUtil.hexDump(buf)); LOGGER.info(message.toString()); diff --git a/src/main/java/org/traccar/handler/TimeHandler.java b/src/main/java/org/traccar/handler/TimeHandler.java index 822c22a0a..3c3e17450 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 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2019 - 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. @@ -19,24 +19,33 @@ import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; 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; +@Singleton @ChannelHandler.Sharable public class TimeHandler extends ChannelInboundHandlerAdapter { + private final boolean enabled; private final boolean useServerTime; private final Set<String> protocols; + @Inject public TimeHandler(Config config) { - useServerTime = config.getString(Keys.TIME_OVERRIDE).equalsIgnoreCase("serverTime"); - String protocolList = Context.getConfig().getString(Keys.TIME_PROTOCOLS); + enabled = config.hasKey(Keys.TIME_OVERRIDE); + if (enabled) { + useServerTime = config.getString(Keys.TIME_OVERRIDE).equalsIgnoreCase("serverTime"); + } else { + useServerTime = false; + } + String protocolList = config.getString(Keys.TIME_PROTOCOLS); if (protocolList != null) { protocols = new HashSet<>(Arrays.asList(protocolList.split("[, ]"))); } else { @@ -47,7 +56,7 @@ public class TimeHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { - if (msg instanceof Position && (protocols == null + if (enabled && msg instanceof Position && (protocols == null || protocols.contains(ctx.pipeline().get(BaseProtocolDecoder.class).getProtocolName()))) { Position position = (Position) msg; diff --git a/src/main/java/org/traccar/handler/events/AlertEventHandler.java b/src/main/java/org/traccar/handler/events/AlertEventHandler.java index 05dbc516e..531a0f957 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 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,18 +21,23 @@ import java.util.Map; import io.netty.channel.ChannelHandler; import org.traccar.config.Config; import org.traccar.config.Keys; -import org.traccar.database.IdentityManager; 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 IdentityManager identityManager; + private final CacheManager cacheManager; private final boolean ignoreDuplicateAlerts; - public AlertEventHandler(Config config, IdentityManager identityManager) { - this.identityManager = identityManager; + @Inject + public AlertEventHandler(Config config, CacheManager cacheManager) { + this.cacheManager = cacheManager; ignoreDuplicateAlerts = config.getBoolean(Keys.EVENT_IGNORE_DUPLICATE_ALERTS); } @@ -42,7 +47,7 @@ public class AlertEventHandler extends BaseEventHandler { if (alarm != null) { boolean ignoreAlert = false; if (ignoreDuplicateAlerts) { - Position lastPosition = identityManager.getLastPosition(position.getDeviceId()); + Position lastPosition = cacheManager.getPosition(position.getDeviceId()); if (lastPosition != null && alarm.equals(lastPosition.getAttributes().get(Position.KEY_ALARM))) { ignoreAlert = true; } diff --git a/src/main/java/org/traccar/handler/events/BaseEventHandler.java b/src/main/java/org/traccar/handler/events/BaseEventHandler.java index 41f677f6c..4a4fb40ff 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 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,17 +18,26 @@ package org.traccar.handler.events; import java.util.Map; import org.traccar.BaseDataHandler; -import org.traccar.Context; +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; + + @Inject + public void setNotificationManager(NotificationManager notificationManager) { + this.notificationManager = notificationManager; + } + @Override protected Position handlePosition(Position position) { Map<Event, Position> events = analyzePosition(position); - if (events != null && Context.getNotificationManager() != null) { - Context.getNotificationManager().updateEvents(events); + if (events != null && !events.isEmpty()) { + notificationManager.updateEvents(events); } return position; } diff --git a/src/main/java/org/traccar/handler/events/BehaviorEventHandler.java b/src/main/java/org/traccar/handler/events/BehaviorEventHandler.java index 767cef3f6..08ae35fcd 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 Anton Tananaev (anton@traccar.org) + * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,32 +18,36 @@ package org.traccar.handler.events; import io.netty.channel.ChannelHandler; import org.traccar.config.Config; import org.traccar.config.Keys; -import org.traccar.database.IdentityManager; import org.traccar.helper.UnitsConverter; 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 BehaviorEventHandler extends BaseEventHandler { private final double accelerationThreshold; private final double brakingThreshold; - private final IdentityManager identityManager; + private final CacheManager cacheManager; - public BehaviorEventHandler(Config config, IdentityManager identityManager) { + @Inject + public BehaviorEventHandler(Config config, CacheManager cacheManager) { accelerationThreshold = config.getDouble(Keys.EVENT_BEHAVIOR_ACCELERATION_THRESHOLD); brakingThreshold = config.getDouble(Keys.EVENT_BEHAVIOR_BRAKING_THRESHOLD); - this.identityManager = identityManager; + this.cacheManager = cacheManager; } @Override protected Map<Event, Position> analyzePosition(Position position) { - Position lastPosition = identityManager.getLastPosition(position.getDeviceId()); + Position lastPosition = cacheManager.getPosition(position.getDeviceId()); if (lastPosition != null && position.getFixTime().equals(lastPosition.getFixTime())) { double acceleration = UnitsConverter.mpsFromKnots(position.getSpeed() - lastPosition.getSpeed()) * 1000 / (position.getFixTime().getTime() - lastPosition.getFixTime().getTime()); diff --git a/src/main/java/org/traccar/handler/events/CommandResultEventHandler.java b/src/main/java/org/traccar/handler/events/CommandResultEventHandler.java index 9b7ff554e..b70f8f33b 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 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,9 +22,17 @@ import io.netty.channel.ChannelHandler; 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 + public CommandResultEventHandler() { + } + @Override protected Map<Event, Position> analyzePosition(Position position) { Object commandResult = position.getAttributes().get(Position.KEY_RESULT); diff --git a/src/main/java/org/traccar/handler/events/DriverEventHandler.java b/src/main/java/org/traccar/handler/events/DriverEventHandler.java index 6fdf4246b..b68327983 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 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org) * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,32 +16,37 @@ */ package org.traccar.handler.events; -import java.util.Collections; -import java.util.Map; - import io.netty.channel.ChannelHandler; -import org.traccar.database.IdentityManager; +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 IdentityManager identityManager; + private final CacheManager cacheManager; - public DriverEventHandler(IdentityManager identityManager) { - this.identityManager = identityManager; + @Inject + public DriverEventHandler(CacheManager cacheManager) { + this.cacheManager = cacheManager; } @Override protected Map<Event, Position> analyzePosition(Position position) { - if (!identityManager.isLatestPosition(position)) { + if (!PositionUtil.isLatest(cacheManager, position)) { return null; } String driverUniqueId = position.getString(Position.KEY_DRIVER_UNIQUE_ID); if (driverUniqueId != null) { String oldDriverUniqueId = null; - Position lastPosition = identityManager.getLastPosition(position.getDeviceId()); + Position lastPosition = cacheManager.getPosition(position.getDeviceId()); if (lastPosition != null) { oldDriverUniqueId = lastPosition.getString(Position.KEY_DRIVER_UNIQUE_ID); } diff --git a/src/main/java/org/traccar/handler/events/FuelDropEventHandler.java b/src/main/java/org/traccar/handler/events/FuelDropEventHandler.java deleted file mode 100644 index 343a17311..000000000 --- a/src/main/java/org/traccar/handler/events/FuelDropEventHandler.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2017 - 2019 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.events; - -import io.netty.channel.ChannelHandler; -import org.traccar.database.IdentityManager; -import org.traccar.model.Device; -import org.traccar.model.Event; -import org.traccar.model.Position; - -import java.util.Collections; -import java.util.Map; - -@ChannelHandler.Sharable -public class FuelDropEventHandler extends BaseEventHandler { - - public static final String ATTRIBUTE_FUEL_DROP_THRESHOLD = "fuelDropThreshold"; - - private final IdentityManager identityManager; - - public FuelDropEventHandler(IdentityManager identityManager) { - this.identityManager = identityManager; - } - - @Override - protected Map<Event, Position> analyzePosition(Position position) { - - Device device = identityManager.getById(position.getDeviceId()); - if (device == null) { - return null; - } - if (!identityManager.isLatestPosition(position)) { - return null; - } - - double fuelDropThreshold = identityManager - .lookupAttributeDouble(device.getId(), ATTRIBUTE_FUEL_DROP_THRESHOLD, 0, true, false); - - if (fuelDropThreshold > 0) { - Position lastPosition = identityManager.getLastPosition(position.getDeviceId()); - if (position.getAttributes().containsKey(Position.KEY_FUEL_LEVEL) - && lastPosition != null && lastPosition.getAttributes().containsKey(Position.KEY_FUEL_LEVEL)) { - - double drop = lastPosition.getDouble(Position.KEY_FUEL_LEVEL) - - position.getDouble(Position.KEY_FUEL_LEVEL); - if (drop >= fuelDropThreshold) { - Event event = new Event(Event.TYPE_DEVICE_FUEL_DROP, position); - event.set(ATTRIBUTE_FUEL_DROP_THRESHOLD, fuelDropThreshold); - return Collections.singletonMap(event, position); - } - } - } - - return null; - } - -} diff --git a/src/main/java/org/traccar/handler/events/FuelEventHandler.java b/src/main/java/org/traccar/handler/events/FuelEventHandler.java new file mode 100644 index 000000000..e5085ecc2 --- /dev/null +++ b/src/main/java/org/traccar/handler/events/FuelEventHandler.java @@ -0,0 +1,79 @@ +/* + * Copyright 2017 - 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.handler.events; + +import io.netty.channel.ChannelHandler; +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.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; + + @Inject + public FuelEventHandler(CacheManager cacheManager) { + this.cacheManager = cacheManager; + } + + @Override + protected Map<Event, Position> analyzePosition(Position position) { + + Device device = cacheManager.getObject(Device.class, position.getDeviceId()); + if (device == null) { + return null; + } + if (!PositionUtil.isLatest(cacheManager, position)) { + return null; + } + + if (position.hasAttribute(Position.KEY_FUEL_LEVEL)) { + Position lastPosition = cacheManager.getPosition(position.getDeviceId()); + if (lastPosition != null && lastPosition.hasAttribute(Position.KEY_FUEL_LEVEL)) { + double before = lastPosition.getDouble(Position.KEY_FUEL_LEVEL); + double after = position.getDouble(Position.KEY_FUEL_LEVEL); + double change = after - before; + + if (change > 0) { + 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); + } + } 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); + } + } + } + } + + 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 dae0c891f..dbe2b8118 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 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 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. @@ -15,75 +15,67 @@ */ package org.traccar.handler.events; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - import io.netty.channel.ChannelHandler; -import org.traccar.database.CalendarManager; -import org.traccar.database.ConnectionManager; -import org.traccar.database.GeofenceManager; -import org.traccar.database.IdentityManager; +import org.traccar.helper.model.PositionUtil; import org.traccar.model.Calendar; -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; +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 IdentityManager identityManager; - private final GeofenceManager geofenceManager; - private final CalendarManager calendarManager; - private final ConnectionManager connectionManager; + private final CacheManager cacheManager; - public GeofenceEventHandler( - IdentityManager identityManager, GeofenceManager geofenceManager, CalendarManager calendarManager, - ConnectionManager connectionManager) { - this.identityManager = identityManager; - this.geofenceManager = geofenceManager; - this.calendarManager = calendarManager; - this.connectionManager = connectionManager; + @Inject + public GeofenceEventHandler(CacheManager cacheManager) { + this.cacheManager = cacheManager; } @Override protected Map<Event, Position> analyzePosition(Position position) { - Device device = identityManager.getById(position.getDeviceId()); - if (device == null) { - return null; - } - if (!identityManager.isLatestPosition(position) || !position.getValid()) { + if (!PositionUtil.isLatest(cacheManager, position)) { return null; } - List<Long> currentGeofences = geofenceManager.getCurrentDeviceGeofences(position); List<Long> oldGeofences = new ArrayList<>(); - if (device.getGeofenceIds() != null) { - oldGeofences.addAll(device.getGeofenceIds()); + Position lastPosition = cacheManager.getPosition(position.getDeviceId()); + if (lastPosition != null && lastPosition.getGeofenceIds() != null) { + oldGeofences.addAll(lastPosition.getGeofenceIds()); } - List<Long> newGeofences = new ArrayList<>(currentGeofences); - newGeofences.removeAll(oldGeofences); - oldGeofences.removeAll(currentGeofences); - device.setGeofenceIds(currentGeofences); - if (!oldGeofences.isEmpty() || !newGeofences.isEmpty()) { - connectionManager.updateDevice(device); + List<Long> newGeofences = new ArrayList<>(); + if (position.getGeofenceIds() != null) { + newGeofences.addAll(position.getGeofenceIds()); + newGeofences.removeAll(oldGeofences); + oldGeofences.removeAll(position.getGeofenceIds()); } Map<Event, Position> events = new HashMap<>(); for (long geofenceId : oldGeofences) { - long calendarId = geofenceManager.getById(geofenceId).getCalendarId(); - Calendar calendar = calendarId != 0 ? calendarManager.getById(calendarId) : null; - if (calendar == null || calendar.checkMoment(position.getFixTime())) { - Event event = new Event(Event.TYPE_GEOFENCE_EXIT, position); - event.setGeofenceId(geofenceId); - events.put(event, position); + Geofence geofence = cacheManager.getObject(Geofence.class, geofenceId); + if (geofence != null) { + long calendarId = geofence.getCalendarId(); + Calendar calendar = calendarId != 0 ? cacheManager.getObject(Calendar.class, calendarId) : null; + if (calendar == null || calendar.checkMoment(position.getFixTime())) { + Event event = new Event(Event.TYPE_GEOFENCE_EXIT, position); + event.setGeofenceId(geofenceId); + events.put(event, position); + } } } for (long geofenceId : newGeofences) { - long calendarId = geofenceManager.getById(geofenceId).getCalendarId(); - Calendar calendar = calendarId != 0 ? calendarManager.getById(calendarId) : null; + long calendarId = cacheManager.getObject(Geofence.class, geofenceId).getCalendarId(); + Calendar calendar = calendarId != 0 ? cacheManager.getObject(Calendar.class, calendarId) : null; if (calendar == null || calendar.checkMoment(position.getFixTime())) { Event event = new Event(Event.TYPE_GEOFENCE_ENTER, position); event.setGeofenceId(geofenceId); diff --git a/src/main/java/org/traccar/handler/events/IgnitionEventHandler.java b/src/main/java/org/traccar/handler/events/IgnitionEventHandler.java index 69df9a46b..ba4159a42 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 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,34 +20,40 @@ import java.util.Collections; import java.util.Map; import io.netty.channel.ChannelHandler; -import org.traccar.database.IdentityManager; +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 IdentityManager identityManager; + private final CacheManager cacheManager; - public IgnitionEventHandler(IdentityManager identityManager) { - this.identityManager = identityManager; + @Inject + public IgnitionEventHandler(CacheManager cacheManager) { + this.cacheManager = cacheManager; } @Override protected Map<Event, Position> analyzePosition(Position position) { - Device device = identityManager.getById(position.getDeviceId()); - if (device == null || !identityManager.isLatestPosition(position)) { + Device device = cacheManager.getObject(Device.class, position.getDeviceId()); + if (device == null || !PositionUtil.isLatest(cacheManager, position)) { return null; } Map<Event, Position> result = null; - if (position.getAttributes().containsKey(Position.KEY_IGNITION)) { + if (position.hasAttribute(Position.KEY_IGNITION)) { boolean ignition = position.getBoolean(Position.KEY_IGNITION); - Position lastPosition = identityManager.getLastPosition(position.getDeviceId()); - if (lastPosition != null && lastPosition.getAttributes().containsKey(Position.KEY_IGNITION)) { + Position lastPosition = cacheManager.getPosition(position.getDeviceId()); + if (lastPosition != null && lastPosition.hasAttribute(Position.KEY_IGNITION)) { boolean oldIgnition = lastPosition.getBoolean(Position.KEY_IGNITION); if (ignition && !oldIgnition) { diff --git a/src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java b/src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java index 0f960ad1f..6c4271ce2 100644 --- a/src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java +++ b/src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) * Copyright 2016 - 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,48 +20,46 @@ import java.util.HashMap; import java.util.Map; import io.netty.channel.ChannelHandler; -import org.traccar.database.IdentityManager; -import org.traccar.database.MaintenancesManager; 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 IdentityManager identityManager; - private final MaintenancesManager maintenancesManager; + private final CacheManager cacheManager; - public MaintenanceEventHandler(IdentityManager identityManager, MaintenancesManager maintenancesManager) { - this.identityManager = identityManager; - this.maintenancesManager = maintenancesManager; + @Inject + public MaintenanceEventHandler(CacheManager cacheManager) { + this.cacheManager = cacheManager; } @Override protected Map<Event, Position> analyzePosition(Position position) { - if (identityManager.getById(position.getDeviceId()) == null - || !identityManager.isLatestPosition(position)) { - return null; - } - - Position lastPosition = identityManager.getLastPosition(position.getDeviceId()); - if (lastPosition == null) { + Position lastPosition = cacheManager.getPosition(position.getDeviceId()); + if (lastPosition == null || position.getFixTime().compareTo(lastPosition.getFixTime()) < 0) { return null; } Map<Event, Position> events = new HashMap<>(); - for (long maintenanceId : maintenancesManager.getAllDeviceItems(position.getDeviceId())) { - Maintenance maintenance = maintenancesManager.getById(maintenanceId); + for (Maintenance maintenance : cacheManager.getDeviceObjects(position.getDeviceId(), Maintenance.class)) { if (maintenance.getPeriod() != 0) { double oldValue = lastPosition.getDouble(maintenance.getType()); double newValue = position.getDouble(maintenance.getType()); - if (oldValue != 0.0 && newValue != 0.0 - && (long) ((oldValue - maintenance.getStart()) / maintenance.getPeriod()) + if (oldValue != 0.0 && newValue != 0.0 && newValue >= maintenance.getStart()) { + if (oldValue < maintenance.getStart() + || (long) ((oldValue - maintenance.getStart()) / maintenance.getPeriod()) < (long) ((newValue - maintenance.getStart()) / maintenance.getPeriod())) { - Event event = new Event(Event.TYPE_MAINTENANCE, position); - event.setMaintenanceId(maintenanceId); - event.set(maintenance.getType(), newValue); - events.put(event, position); + Event event = new Event(Event.TYPE_MAINTENANCE, position); + event.setMaintenanceId(maintenance.getId()); + event.set(maintenance.getType(), newValue); + events.put(event, position); + } } } } diff --git a/src/main/java/org/traccar/handler/events/MediaEventHandler.java b/src/main/java/org/traccar/handler/events/MediaEventHandler.java new file mode 100644 index 000000000..52d8e6961 --- /dev/null +++ b/src/main/java/org/traccar/handler/events/MediaEventHandler.java @@ -0,0 +1,49 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.handler.events; + +import io.netty.channel.ChannelHandler; +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 + public MediaEventHandler() { + } + + @Override + protected Map<Event, Position> analyzePosition(Position position) { + return Stream.of(Position.KEY_IMAGE, Position.KEY_VIDEO, Position.KEY_AUDIO) + .filter(position::hasAttribute) + .map(type -> { + Event event = new Event(Event.TYPE_MEDIA, position); + event.set("media", type); + event.set("file", position.getString(type)); + return event; + }) + .collect(Collectors.toMap(event -> event, event -> position)); + } + +} diff --git a/src/main/java/org/traccar/handler/events/MotionEventHandler.java b/src/main/java/org/traccar/handler/events/MotionEventHandler.java index db276f32b..15902d6d4 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 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,120 +16,73 @@ */ package org.traccar.handler.events; -import java.util.Collections; -import java.util.Map; - import io.netty.channel.ChannelHandler; -import org.traccar.database.DeviceManager; -import org.traccar.database.IdentityManager; +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.DeviceState; import org.traccar.model.Event; import org.traccar.model.Position; -import org.traccar.reports.ReportUtils; -import org.traccar.reports.model.TripsConfig; +import org.traccar.reports.common.TripsConfig; +import org.traccar.session.cache.CacheManager; +import org.traccar.session.state.MotionProcessor; +import org.traccar.session.state.MotionState; +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.Collections; +import java.util.Map; +@Singleton @ChannelHandler.Sharable public class MotionEventHandler extends BaseEventHandler { - private final IdentityManager identityManager; - private final DeviceManager deviceManager; - private final TripsConfig tripsConfig; - - public MotionEventHandler(IdentityManager identityManager, DeviceManager deviceManager, TripsConfig tripsConfig) { - this.identityManager = identityManager; - this.deviceManager = deviceManager; - this.tripsConfig = tripsConfig; - } - - private Map<Event, Position> newEvent(DeviceState deviceState, boolean newMotion) { - String eventType = newMotion ? Event.TYPE_DEVICE_MOVING : Event.TYPE_DEVICE_STOPPED; - Position position = deviceState.getMotionPosition(); - Event event = new Event(eventType, position); - deviceState.setMotionState(newMotion); - deviceState.setMotionPosition(null); - return Collections.singletonMap(event, position); - } + private static final Logger LOGGER = LoggerFactory.getLogger(MotionEventHandler.class); - public Map<Event, Position> updateMotionState(DeviceState deviceState) { - Map<Event, Position> result = null; - if (deviceState.getMotionState() != null && deviceState.getMotionPosition() != null) { - boolean newMotion = !deviceState.getMotionState(); - Position motionPosition = deviceState.getMotionPosition(); - long currentTime = System.currentTimeMillis(); - long motionTime = motionPosition.getFixTime().getTime() - + (newMotion ? tripsConfig.getMinimalTripDuration() : tripsConfig.getMinimalParkingDuration()); - if (motionTime <= currentTime) { - result = newEvent(deviceState, newMotion); - } - } - return result; - } + private final CacheManager cacheManager; + private final Storage storage; - public Map<Event, Position> updateMotionState(DeviceState deviceState, Position position) { - return updateMotionState(deviceState, position, position.getBoolean(Position.KEY_MOTION)); - } - - public Map<Event, Position> updateMotionState(DeviceState deviceState, Position position, boolean newMotion) { - Map<Event, Position> result = null; - Boolean oldMotion = deviceState.getMotionState(); - - long currentTime = position.getFixTime().getTime(); - if (newMotion != oldMotion) { - if (deviceState.getMotionPosition() == null) { - deviceState.setMotionPosition(position); - } - } else { - deviceState.setMotionPosition(null); - } - - Position motionPosition = deviceState.getMotionPosition(); - if (motionPosition != null) { - long motionTime = motionPosition.getFixTime().getTime(); - double distance = ReportUtils.calculateDistance(motionPosition, position, false); - Boolean ignition = null; - if (tripsConfig.getUseIgnition() - && position.getAttributes().containsKey(Position.KEY_IGNITION)) { - ignition = position.getBoolean(Position.KEY_IGNITION); - } - if (newMotion) { - if (motionTime + tripsConfig.getMinimalTripDuration() <= currentTime - || distance >= tripsConfig.getMinimalTripDistance()) { - result = newEvent(deviceState, newMotion); - } - } else { - if (motionTime + tripsConfig.getMinimalParkingDuration() <= currentTime - || ignition != null && !ignition) { - result = newEvent(deviceState, newMotion); - } - } - } - return result; + @Inject + public MotionEventHandler(CacheManager cacheManager, Storage storage) { + this.cacheManager = cacheManager; + this.storage = storage; } @Override protected Map<Event, Position> analyzePosition(Position position) { long deviceId = position.getDeviceId(); - Device device = identityManager.getById(deviceId); - if (device == null) { + Device device = cacheManager.getObject(Device.class, deviceId); + if (device == null || !PositionUtil.isLatest(cacheManager, position)) { return null; } - if (!identityManager.isLatestPosition(position) - || !tripsConfig.getProcessInvalidPositions() && !position.getValid()) { + boolean processInvalid = AttributeUtil.lookup( + cacheManager, Keys.EVENT_MOTION_PROCESS_INVALID_POSITIONS, deviceId); + if (!processInvalid && !position.getValid()) { return null; } - Map<Event, Position> result = null; - DeviceState deviceState = deviceManager.getDeviceState(deviceId); - - if (deviceState.getMotionState() == null) { - deviceState.setMotionState(position.getBoolean(Position.KEY_MOTION)); - } else { - result = updateMotionState(deviceState, position); + TripsConfig tripsConfig = new TripsConfig(new AttributeUtil.CacheProvider(cacheManager, deviceId)); + MotionState state = MotionState.fromDevice(device); + MotionProcessor.updateState(state, position, position.getBoolean(Position.KEY_MOTION), tripsConfig); + if (state.isChanged()) { + state.toDevice(device); + try { + storage.updateObject(device, new Request( + new Columns.Include("motionStreak", "motionState", "motionTime", "motionDistance"), + new Condition.Equals("id", device.getId()))); + } catch (StorageException e) { + LOGGER.warn("Update device motion error", e); + } } - deviceManager.setDeviceState(deviceId, deviceState); - return result; + return state.getEvent() != null ? Collections.singletonMap(state.getEvent(), position) : null; } } diff --git a/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java b/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java index 347ad9005..3bb5f713c 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 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,111 +16,67 @@ */ package org.traccar.handler.events; -import java.util.Collections; -import java.util.Map; - import io.netty.channel.ChannelHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.traccar.config.Config; import org.traccar.config.Keys; -import org.traccar.database.DeviceManager; -import org.traccar.database.GeofenceManager; +import org.traccar.helper.model.AttributeUtil; +import org.traccar.helper.model.PositionUtil; import org.traccar.model.Device; -import org.traccar.model.DeviceState; import org.traccar.model.Event; import org.traccar.model.Geofence; import org.traccar.model.Position; +import org.traccar.session.cache.CacheManager; +import org.traccar.session.state.OverspeedProcessor; +import org.traccar.session.state.OverspeedState; +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.Collections; +import java.util.Map; +@Singleton @ChannelHandler.Sharable public class OverspeedEventHandler extends BaseEventHandler { - public static final String ATTRIBUTE_SPEED = "speed"; - public static final String ATTRIBUTE_SPEED_LIMIT = "speedLimit"; + private static final Logger LOGGER = LoggerFactory.getLogger(OverspeedEventHandler.class); - private final DeviceManager deviceManager; - private final GeofenceManager geofenceManager; + private final CacheManager cacheManager; + private final Storage storage; - private final boolean notRepeat; private final long minimalDuration; private final boolean preferLowest; + private final double multiplier; - public OverspeedEventHandler(Config config, DeviceManager deviceManager, GeofenceManager geofenceManager) { - this.deviceManager = deviceManager; - this.geofenceManager = geofenceManager; - notRepeat = config.getBoolean(Keys.EVENT_OVERSPEED_NOT_REPEAT); + @Inject + public OverspeedEventHandler( + Config config, CacheManager cacheManager, Storage storage) { + this.cacheManager = cacheManager; + this.storage = storage; minimalDuration = config.getLong(Keys.EVENT_OVERSPEED_MINIMAL_DURATION) * 1000; preferLowest = config.getBoolean(Keys.EVENT_OVERSPEED_PREFER_LOWEST); - } - - private Map<Event, Position> newEvent(DeviceState deviceState, double speedLimit) { - Position position = deviceState.getOverspeedPosition(); - Event event = new Event(Event.TYPE_DEVICE_OVERSPEED, position); - event.set(ATTRIBUTE_SPEED, deviceState.getOverspeedPosition().getSpeed()); - event.set(ATTRIBUTE_SPEED_LIMIT, speedLimit); - event.setGeofenceId(deviceState.getOverspeedGeofenceId()); - deviceState.setOverspeedState(notRepeat); - deviceState.setOverspeedPosition(null); - deviceState.setOverspeedGeofenceId(0); - return Collections.singletonMap(event, position); - } - - public Map<Event, Position> updateOverspeedState(DeviceState deviceState, double speedLimit) { - Map<Event, Position> result = null; - if (deviceState.getOverspeedState() != null && !deviceState.getOverspeedState() - && deviceState.getOverspeedPosition() != null && speedLimit != 0) { - long currentTime = System.currentTimeMillis(); - Position overspeedPosition = deviceState.getOverspeedPosition(); - long overspeedTime = overspeedPosition.getFixTime().getTime(); - if (overspeedTime + minimalDuration <= currentTime) { - result = newEvent(deviceState, speedLimit); - } - } - return result; - } - - public Map<Event, Position> updateOverspeedState( - DeviceState deviceState, Position position, double speedLimit, long geofenceId) { - Map<Event, Position> result = null; - - Boolean oldOverspeed = deviceState.getOverspeedState(); - - long currentTime = position.getFixTime().getTime(); - boolean newOverspeed = position.getSpeed() > speedLimit; - if (newOverspeed && !oldOverspeed) { - if (deviceState.getOverspeedPosition() == null) { - deviceState.setOverspeedPosition(position); - deviceState.setOverspeedGeofenceId(geofenceId); - } - } else if (oldOverspeed && !newOverspeed) { - deviceState.setOverspeedState(false); - deviceState.setOverspeedPosition(null); - deviceState.setOverspeedGeofenceId(0); - } else { - deviceState.setOverspeedPosition(null); - deviceState.setOverspeedGeofenceId(0); - } - Position overspeedPosition = deviceState.getOverspeedPosition(); - if (overspeedPosition != null) { - long overspeedTime = overspeedPosition.getFixTime().getTime(); - if (newOverspeed && overspeedTime + minimalDuration <= currentTime) { - result = newEvent(deviceState, speedLimit); - } - } - return result; + multiplier = config.getDouble(Keys.EVENT_OVERSPEED_THRESHOLD_MULTIPLIER); } @Override protected Map<Event, Position> analyzePosition(Position position) { long deviceId = position.getDeviceId(); - Device device = deviceManager.getById(deviceId); + Device device = cacheManager.getObject(Device.class, position.getDeviceId()); if (device == null) { return null; } - if (!deviceManager.isLatestPosition(position) || !position.getValid()) { + if (!PositionUtil.isLatest(cacheManager, position) || !position.getValid()) { return null; } - double speedLimit = deviceManager.lookupAttributeDouble(deviceId, ATTRIBUTE_SPEED_LIMIT, 0, true, false); + double speedLimit = AttributeUtil.lookup(cacheManager, Keys.EVENT_OVERSPEED_LIMIT, deviceId); double positionSpeedLimit = position.getDouble(Position.KEY_SPEED_LIMIT); if (positionSpeedLimit > 0) { @@ -130,11 +86,11 @@ public class OverspeedEventHandler extends BaseEventHandler { double geofenceSpeedLimit = 0; long overspeedGeofenceId = 0; - if (geofenceManager != null && device.getGeofenceIds() != null) { - for (long geofenceId : device.getGeofenceIds()) { - Geofence geofence = geofenceManager.getById(geofenceId); + if (position.getGeofenceIds() != null) { + for (long geofenceId : position.getGeofenceIds()) { + Geofence geofence = cacheManager.getObject(Geofence.class, geofenceId); if (geofence != null) { - double currentSpeedLimit = geofence.getDouble(ATTRIBUTE_SPEED_LIMIT); + double currentSpeedLimit = geofence.getDouble(Keys.EVENT_OVERSPEED_LIMIT.getKey()); if (currentSpeedLimit > 0 && geofenceSpeedLimit == 0 || preferLowest && currentSpeedLimit < geofenceSpeedLimit || !preferLowest && currentSpeedLimit > geofenceSpeedLimit) { @@ -152,18 +108,19 @@ public class OverspeedEventHandler extends BaseEventHandler { return null; } - Map<Event, Position> result = null; - DeviceState deviceState = deviceManager.getDeviceState(deviceId); - - if (deviceState.getOverspeedState() == null) { - deviceState.setOverspeedState(position.getSpeed() > speedLimit); - deviceState.setOverspeedGeofenceId(position.getSpeed() > speedLimit ? overspeedGeofenceId : 0); - } else { - result = updateOverspeedState(deviceState, position, speedLimit, overspeedGeofenceId); + OverspeedState state = OverspeedState.fromDevice(device); + OverspeedProcessor.updateState(state, position, speedLimit, multiplier, minimalDuration, overspeedGeofenceId); + if (state.isChanged()) { + state.toDevice(device); + try { + storage.updateObject(device, new Request( + new Columns.Include("overspeedState", "overspeedTime", "overspeedGeofenceId"), + new Condition.Equals("id", device.getId()))); + } catch (StorageException e) { + LOGGER.warn("Update device overspeed error", e); + } } - - deviceManager.setDeviceState(deviceId, deviceState); - return result; + return state.getEvent() != null ? Collections.singletonMap(state.getEvent(), position) : null; } } diff --git a/src/main/java/org/traccar/helper/BitUtil.java b/src/main/java/org/traccar/helper/BitUtil.java index b6108edff..829ddebc9 100644 --- a/src/main/java/org/traccar/helper/BitUtil.java +++ b/src/main/java/org/traccar/helper/BitUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Anton Tananaev (anton@traccar.org) + * 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. @@ -21,7 +21,7 @@ public final class BitUtil { } public static boolean check(long number, int index) { - return (number & (1 << index)) != 0; + return (number & (1L << index)) != 0; } public static int between(int number, int from, int to) { diff --git a/src/main/java/org/traccar/helper/BufferUtil.java b/src/main/java/org/traccar/helper/BufferUtil.java index 9485c17c6..12c31ba9d 100644 --- a/src/main/java/org/traccar/helper/BufferUtil.java +++ b/src/main/java/org/traccar/helper/BufferUtil.java @@ -27,6 +27,24 @@ public final class BufferUtil { private BufferUtil() { } + public static int readSignedMagnitudeInt(ByteBuf buffer) { + long value = buffer.readUnsignedInt(); + int result = (int) BitUtil.to(value, 31); + return BitUtil.check(value, 31) ? -result : result; + } + + public static int indexOf(ByteBuf buffer, int fromIndex, int toIndex, byte value, int count) { + int startIndex = fromIndex; + for (int i = 0; i < count; i++) { + int result = buffer.indexOf(startIndex, toIndex, value); + if (result < 0 || i == count - 1) { + return result; + } + startIndex = result + 1; + } + return -1; + } + public static int indexOf(String needle, ByteBuf haystack) { return indexOf(needle, haystack, haystack.readerIndex(), haystack.writerIndex()); } @@ -41,16 +59,28 @@ public final class BufferUtil { } public static int indexOf(ByteBuf needle, ByteBuf haystack, int startIndex, int endIndex) { - ByteBuf wrappedHaystack; - if (startIndex == haystack.readerIndex() && endIndex == haystack.writerIndex()) { - wrappedHaystack = haystack; - } else { - wrappedHaystack = Unpooled.wrappedBuffer(haystack); - wrappedHaystack.readerIndex(startIndex - haystack.readerIndex()); - wrappedHaystack.writerIndex(endIndex - haystack.readerIndex()); + int originalReaderIndex = haystack.readerIndex(); + int originalWriterIndex = haystack.writerIndex(); + try { + haystack.readerIndex(startIndex); + haystack.writerIndex(endIndex); + return ByteBufUtil.indexOf(needle, haystack); + } finally { + haystack.readerIndex(originalReaderIndex); + haystack.writerIndex(originalWriterIndex); + } + } + + public static boolean isPrintable(ByteBuf buf, int length) { + boolean printable = true; + for (int i = 0; i < length; i++) { + byte b = buf.getByte(buf.readerIndex() + i); + if (b < 32 && b != '\r' && b != '\n') { + printable = false; + break; + } } - int result = ByteBufUtil.indexOf(needle, wrappedHaystack); - return result < 0 ? result : startIndex + result; + return printable; } } diff --git a/src/main/java/org/traccar/helper/Checksum.java b/src/main/java/org/traccar/helper/Checksum.java index 8c3d0063a..db5817275 100644 --- a/src/main/java/org/traccar/helper/Checksum.java +++ b/src/main/java/org/traccar/helper/Checksum.java @@ -200,4 +200,19 @@ public final class Checksum { return (10 - (checksum % 10)) % 10; } + public static int ip(ByteBuffer data) { + int sum = 0; + while (data.remaining() > 0) { + sum += data.get() & 0xff; + if ((sum & 0x80000000) > 0) { + sum = (sum & 0xffff) + (sum >> 16); + } + } + while ((sum >> 16) > 0) { + sum = (sum & 0xffff) + sum >> 16; + } + sum = (sum == 0xffff) ? sum & 0xffff : (~sum) & 0xffff; + return sum; + } + } diff --git a/src/main/java/org/traccar/helper/ClassScanner.java b/src/main/java/org/traccar/helper/ClassScanner.java index c928f6a12..c201d101f 100644 --- a/src/main/java/org/traccar/helper/ClassScanner.java +++ b/src/main/java/org/traccar/helper/ClassScanner.java @@ -46,7 +46,7 @@ public final class ClassScanner { URL packageUrl = baseClass.getClassLoader().getResource(packagePath); if (packageUrl.getProtocol().equals("jar")) { - String jarFileName = URLDecoder.decode(packageUrl.getFile(), StandardCharsets.UTF_8.name()); + String jarFileName = URLDecoder.decode(packageUrl.getFile(), StandardCharsets.UTF_8); try (JarFile jf = new JarFile(jarFileName.substring(5, jarFileName.indexOf("!")))) { Enumeration<JarEntry> jarEntries = jf.entries(); while (jarEntries.hasMoreElements()) { diff --git a/src/main/java/org/traccar/helper/Log.java b/src/main/java/org/traccar/helper/Log.java index 8c67f9ddc..9aaf1cfd3 100644 --- a/src/main/java/org/traccar/helper/Log.java +++ b/src/main/java/org/traccar/helper/Log.java @@ -28,6 +28,10 @@ import java.io.StringWriter; import java.io.Writer; import java.net.URL; import java.nio.charset.StandardCharsets; +import java.nio.file.FileStore; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; import java.text.SimpleDateFormat; import java.util.Date; import java.util.logging.ConsoleHandler; @@ -51,10 +55,12 @@ public final class Log { private String suffix; private Writer writer; private final boolean rotate; + private final String template; - RollingFileHandler(String name, boolean rotate) { + RollingFileHandler(String name, boolean rotate, String rotateInterval) { this.name = name; this.rotate = rotate; + this.template = rotateInterval.equalsIgnoreCase("HOUR") ? "yyyyMMddHH" : "yyyyMMdd"; } @Override @@ -63,7 +69,7 @@ public final class Log { try { String suffix = ""; if (rotate) { - suffix = new SimpleDateFormat("yyyyMMdd").format(new Date(record.getMillis())); + suffix = new SimpleDateFormat(template).format(new Date(record.getMillis())); if (writer != null && !suffix.equals(this.suffix)) { writer.close(); writer = null; @@ -165,7 +171,7 @@ public final class Log { public static void setupDefaultLogger() { String path = null; - URL url = ClassLoader.getSystemClassLoader().getResource("."); + URL url = ClassLoader.getSystemClassLoader().getResource("."); if (url != null) { File jarPath = new File(url.getPath()); File logsPath = new File(jarPath, "logs"); @@ -174,7 +180,7 @@ public final class Log { } path = new File(logsPath, "tracker-server.log").getPath(); } - setupLogger(path == null, path, Level.WARNING.getName(), false, true); + setupLogger(path == null, path, Level.WARNING.getName(), false, true, "DAY"); } public static void setupLogger(Config config) { @@ -183,11 +189,13 @@ public final class Log { config.getString(Keys.LOGGER_FILE), config.getString(Keys.LOGGER_LEVEL), config.getBoolean(Keys.LOGGER_FULL_STACK_TRACES), - config.getBoolean(Keys.LOGGER_ROTATE)); + config.getBoolean(Keys.LOGGER_ROTATE), + config.getString(Keys.LOGGER_ROTATE_INTERVAL)); } private static void setupLogger( - boolean console, String file, String levelString, boolean fullStackTraces, boolean rotate) { + boolean console, String file, String levelString, + boolean fullStackTraces, boolean rotate, String rotateInterval) { Logger rootLogger = Logger.getLogger(""); for (Handler handler : rootLogger.getHandlers()) { @@ -198,7 +206,7 @@ public final class Log { if (console) { handler = new ConsoleHandler(); } else { - handler = new RollingFileHandler(file, rotate); + handler = new RollingFileHandler(file, rotate, rotateInterval); } handler.setFormatter(new LogFormatter(fullStackTraces)); @@ -269,4 +277,18 @@ public final class Log { return s.toString(); } + public static long[] getStorageSpace() { + long usable = 0; + long total = 0; + for (Path root : FileSystems.getDefault().getRootDirectories()) { + try { + FileStore store = Files.getFileStore(root); + usable += store.getUsableSpace(); + total += store.getTotalSpace(); + } catch (IOException ignored) { + } + } + return new long[]{usable, total}; + } + } diff --git a/src/main/java/org/traccar/helper/LogAction.java b/src/main/java/org/traccar/helper/LogAction.java index d16b25483..b255b9206 100644 --- a/src/main/java/org/traccar/helper/LogAction.java +++ b/src/main/java/org/traccar/helper/LogAction.java @@ -47,7 +47,7 @@ public final class LogAction { private static final String PATTERN_OBJECT = "user: %d, action: %s, object: %s, id: %d"; private static final String PATTERN_LINK = "user: %d, action: %s, owner: %s, id: %d, property: %s, id: %d"; - private static final String PATTERN_LOGIN = "user: %d, action: %s"; + private static final String PATTERN_LOGIN = "user: %d, action: %s, from: %s"; private static final String PATTERN_LOGIN_FAILED = "login failed from: %s"; private static final String PATTERN_DEVICE_ACCUMULATORS = "user: %d, action: %s, deviceId: %d"; private static final String PATTERN_REPORT = "user: %d, report: %s, from: %s, to: %s, devices: %s, groups: %s"; @@ -72,12 +72,12 @@ public final class LogAction { logLinkAction(ACTION_UNLINK, userId, owner, ownerId, property, propertyId); } - public static void login(long userId) { - logLoginAction(ACTION_LOGIN, userId); + public static void login(long userId, String remoteAddress) { + logLoginAction(ACTION_LOGIN, userId, remoteAddress); } - public static void logout(long userId) { - logLoginAction(ACTION_LOGOUT, userId); + public static void logout(long userId, String remoteAddress) { + logLoginAction(ACTION_LOGOUT, userId, remoteAddress); } public static void failedLogin(String remoteAddress) { @@ -105,8 +105,11 @@ public final class LogAction { Introspector.decapitalize(property.getSimpleName()), propertyId)); } - private static void logLoginAction(String action, long userId) { - LOGGER.info(String.format(PATTERN_LOGIN, userId, action)); + private static void logLoginAction(String action, long userId, String remoteAddress) { + if (remoteAddress == null || remoteAddress.isEmpty()) { + remoteAddress = "unknown"; + } + LOGGER.info(String.format(PATTERN_LOGIN, userId, action, remoteAddress)); } public static void logReport( diff --git a/src/main/java/org/traccar/helper/NetworkUtil.java b/src/main/java/org/traccar/helper/NetworkUtil.java new file mode 100644 index 000000000..2fe3487da --- /dev/null +++ b/src/main/java/org/traccar/helper/NetworkUtil.java @@ -0,0 +1,31 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.helper; + +import io.netty.channel.Channel; +import io.netty.channel.socket.DatagramChannel; + +public final class NetworkUtil { + + private NetworkUtil() { + } + + public static String session(Channel channel) { + char transport = channel instanceof DatagramChannel ? 'U' : 'T'; + return transport + channel.id().asShortText(); + } + +} diff --git a/src/main/java/org/traccar/helper/ObdDecoder.java b/src/main/java/org/traccar/helper/ObdDecoder.java index b22065f4e..3cbae334a 100644 --- a/src/main/java/org/traccar/helper/ObdDecoder.java +++ b/src/main/java/org/traccar/helper/ObdDecoder.java @@ -51,22 +51,7 @@ public final class ObdDecoder { StringBuilder codes = new StringBuilder(); for (int i = 0; i < value.length() / 4; i++) { int numValue = Integer.parseInt(value.substring(i * 4, (i + 1) * 4), 16); - codes.append(' '); - switch (numValue >> 14) { - case 1: - codes.append('C'); - break; - case 2: - codes.append('B'); - break; - case 3: - codes.append('U'); - break; - default: - codes.append('P'); - break; - } - codes.append(String.format("%04X", numValue & 0x3FFF)); + codes.append(' ').append(decodeCode(numValue)); } if (codes.length() > 0) { return createEntry(Position.KEY_DTCS, codes.toString().trim()); @@ -75,6 +60,25 @@ public final class ObdDecoder { } } + public static String decodeCode(int value) { + char prefix; + switch (value >> 14) { + case 1: + prefix = 'C'; + break; + case 2: + prefix = 'B'; + break; + case 3: + prefix = 'U'; + break; + default: + prefix = 'P'; + break; + } + return String.format("%c%04X", prefix, value & 0x3FFF); + } + public static Map.Entry<String, Object> decodeData(int pid, long value, boolean convert) { switch (pid) { case 0x04: diff --git a/src/main/java/org/traccar/helper/ObjectMapperContextResolver.java b/src/main/java/org/traccar/helper/ObjectMapperContextResolver.java new file mode 100644 index 000000000..634950b85 --- /dev/null +++ b/src/main/java/org/traccar/helper/ObjectMapperContextResolver.java @@ -0,0 +1,38 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.helper; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import jakarta.inject.Inject; +import jakarta.ws.rs.ext.ContextResolver; + +// This does not work as a lambda +public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> { + + private final ObjectMapper objectMapper; + + @Inject + public ObjectMapperContextResolver(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + @Override + public ObjectMapper getContext(Class<?> clazz) { + return objectMapper; + } + +} diff --git a/src/main/java/org/traccar/helper/Parser.java b/src/main/java/org/traccar/helper/Parser.java index 75106e2ba..c2aea28fa 100644 --- a/src/main/java/org/traccar/helper/Parser.java +++ b/src/main/java/org/traccar/helper/Parser.java @@ -48,13 +48,25 @@ public class Parser { } public boolean hasNext(int number) { - String value = matcher.group(position); - if (value != null && !value.isEmpty()) { - return true; - } else { - position += number; - return false; + for (int i = position; i < position + number; i++) { + String value = matcher.group(i); + if (value == null || value.isEmpty()) { + position += number; + return false; + } + } + return true; + } + + public boolean hasNextAny(int number) { + for (int i = position; i < position + number; i++) { + String value = matcher.group(i); + if (value != null && !value.isEmpty()) { + return true; + } } + position += number; + return false; } public String next() { @@ -155,6 +167,7 @@ public class Parser { public enum CoordinateFormat { DEG_DEG, + DEG_DEG_HEM, DEG_HEM, DEG_MIN_MIN, DEG_MIN_HEM, @@ -173,6 +186,10 @@ public class Parser { case DEG_DEG: coordinate = Double.parseDouble(next() + '.' + next()); break; + case DEG_DEG_HEM: + coordinate = Double.parseDouble(next() + '.' + next()); + hemisphere = next(); + break; case DEG_HEM: coordinate = nextDouble(0); hemisphere = next(); diff --git a/src/main/java/org/traccar/helper/PatternUtil.java b/src/main/java/org/traccar/helper/PatternUtil.java index 74813e1d9..a46c7b7b4 100644 --- a/src/main/java/org/traccar/helper/PatternUtil.java +++ b/src/main/java/org/traccar/helper/PatternUtil.java @@ -63,7 +63,7 @@ public final class PatternUtil { for (int i = 0; i < pattern.length(); i++) { try { - Matcher matcher = Pattern.compile("(" + pattern.substring(0, i) + ").*").matcher(input); + Matcher matcher = Pattern.compile("(" + pattern.substring(0, i) + ")[\\s\\S]*").matcher(input); if (matcher.matches()) { result.patternMatch = pattern.substring(0, i); result.patternTail = pattern.substring(i); diff --git a/src/main/java/org/traccar/helper/StringUtil.java b/src/main/java/org/traccar/helper/StringUtil.java new file mode 100644 index 000000000..9b4d717f4 --- /dev/null +++ b/src/main/java/org/traccar/helper/StringUtil.java @@ -0,0 +1,32 @@ +/* + * 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; + +public final class StringUtil { + + private StringUtil() { + } + + public static boolean containsHex(String value) { + for (char c : value.toCharArray()) { + if (c >= 'a' && c <= 'f' || c >= 'A' && c <= 'F') { + return true; + } + } + return false; + } + +} diff --git a/src/main/java/org/traccar/helper/ServletHelper.java b/src/main/java/org/traccar/helper/WebHelper.java index b6c587ec3..9533fe84b 100644 --- a/src/main/java/org/traccar/helper/ServletHelper.java +++ b/src/main/java/org/traccar/helper/WebHelper.java @@ -15,11 +15,18 @@ */ package org.traccar.helper; -import javax.servlet.http.HttpServletRequest; +import java.net.InetAddress; +import java.net.UnknownHostException; -public final class ServletHelper { +import jakarta.servlet.http.HttpServletRequest; - private ServletHelper() { +import org.eclipse.jetty.util.URIUtil; +import org.traccar.config.Config; +import org.traccar.config.Keys; + +public final class WebHelper { + + private WebHelper() { } public static String retrieveRemoteAddress(HttpServletRequest request) { @@ -42,4 +49,17 @@ public final class ServletHelper { } } + public static String retrieveWebUrl(Config config) { + if (config.hasKey(Keys.WEB_URL)) { + return config.getString(Keys.WEB_URL).replaceAll("/$", ""); + } else { + String address; + try { + address = config.getString(Keys.WEB_ADDRESS, InetAddress.getLocalHost().getHostAddress()); + } catch (UnknownHostException e) { + address = "localhost"; + } + return URIUtil.newURI("http", address, config.getInteger(Keys.WEB_PORT), "", ""); + } + } } diff --git a/src/main/java/org/traccar/helper/model/AttributeUtil.java b/src/main/java/org/traccar/helper/model/AttributeUtil.java new file mode 100644 index 000000000..2630f64f0 --- /dev/null +++ b/src/main/java/org/traccar/helper/model/AttributeUtil.java @@ -0,0 +1,188 @@ +/* + * 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.helper.model; + +import org.traccar.api.security.PermissionsService; +import org.traccar.config.Config; +import org.traccar.config.ConfigKey; +import org.traccar.config.KeyType; +import org.traccar.config.Keys; +import org.traccar.model.Device; +import org.traccar.model.Group; +import org.traccar.model.Server; +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 final class AttributeUtil { + + private AttributeUtil() { + } + + public interface Provider { + Device getDevice(); + Group getGroup(long groupId); + Server getServer(); + Config getConfig(); + } + + public static <T> T lookup(CacheManager cacheManager, ConfigKey<T> key, long deviceId) { + return lookup(new CacheProvider(cacheManager, deviceId), key); + } + + @SuppressWarnings({ "deprecation", "unchecked" }) + public static <T> T lookup(Provider provider, ConfigKey<T> key) { + Device device = provider.getDevice(); + Object result = device.getAttributes().get(key.getKey()); + long groupId = device.getGroupId(); + while (result == null && groupId > 0) { + Group group = provider.getGroup(groupId); + if (group != null) { + result = group.getAttributes().get(key.getKey()); + groupId = group.getGroupId(); + } else { + groupId = 0; + } + } + if (result == null && key.hasType(KeyType.SERVER)) { + result = provider.getServer().getAttributes().get(key.getKey()); + } + if (result == null && key.hasType(KeyType.CONFIG)) { + result = provider.getConfig().getString(key.getKey()); + } + + if (result != null) { + Class<T> valueClass = key.getValueClass(); + if (valueClass.equals(Boolean.class)) { + return (T) (result instanceof String + ? Boolean.parseBoolean((String) result) + : result); + } else if (valueClass.equals(Integer.class)) { + return (T) (Object) (result instanceof String + ? Integer.parseInt((String) result) + : ((Number) result).intValue()); + } else if (valueClass.equals(Long.class)) { + return (T) (Object) (result instanceof String + ? Long.parseLong((String) result) + : ((Number) result).longValue()); + } else if (valueClass.equals(Double.class)) { + return (T) (Object) (result instanceof String + ? Double.parseDouble((String) result) + : ((Number) result).doubleValue()); + } else { + return (T) result; + } + } + return key.getDefaultValue(); + } + + public static String getDevicePassword( + CacheManager cacheManager, long deviceId, String protocol, String defaultPassword) { + + String password = lookup(cacheManager, Keys.DEVICE_PASSWORD, deviceId); + if (password != null) { + return password; + } + + if (protocol != null) { + password = cacheManager.getConfig().getString(Keys.PROTOCOL_DEVICE_PASSWORD.withPrefix(protocol)); + if (password != null) { + return password; + } + } + + return defaultPassword; + } + + public static class CacheProvider implements Provider { + + private final CacheManager cacheManager; + private final long deviceId; + + public CacheProvider(CacheManager cacheManager, long deviceId) { + this.cacheManager = cacheManager; + this.deviceId = deviceId; + } + + @Override + public Device getDevice() { + return cacheManager.getObject(Device.class, deviceId); + } + + @Override + public Group getGroup(long groupId) { + return cacheManager.getObject(Group.class, groupId); + } + + @Override + public Server getServer() { + return cacheManager.getServer(); + } + + @Override + public Config getConfig() { + return cacheManager.getConfig(); + } + } + + public static class StorageProvider implements Provider { + + private final Config config; + private final Storage storage; + private final PermissionsService permissionsService; + private final Device device; + + public StorageProvider(Config config, Storage storage, PermissionsService permissionsService, Device device) { + this.config = config; + this.storage = storage; + this.permissionsService = permissionsService; + this.device = device; + } + + @Override + public Device getDevice() { + return device; + } + + @Override + public Group getGroup(long groupId) { + try { + return storage.getObject( + Group.class, new Request(new Columns.All(), new Condition.Equals("id", groupId))); + } catch (StorageException e) { + throw new RuntimeException(e); + } + } + + @Override + public Server getServer() { + try { + return permissionsService.getServer(); + } catch (StorageException e) { + throw new RuntimeException(e); + } + } + + @Override + public Config getConfig() { + return config; + } + } + +} diff --git a/src/main/java/org/traccar/helper/model/DeviceUtil.java b/src/main/java/org/traccar/helper/model/DeviceUtil.java new file mode 100644 index 000000000..5d8cb5f25 --- /dev/null +++ b/src/main/java/org/traccar/helper/model/DeviceUtil.java @@ -0,0 +1,79 @@ +/* + * 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.helper.model; + +import org.traccar.model.Device; +import org.traccar.model.Group; +import org.traccar.model.User; +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 java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.Objects; +import java.util.stream.Collectors; + +public final class DeviceUtil { + + private DeviceUtil() { + } + + public static void resetStatus(Storage storage) throws StorageException { + storage.updateObject(new Device(), new Request(new Columns.Include("status"))); + } + + + public static Collection<Device> getAccessibleDevices( + Storage storage, long userId, + Collection<Long> deviceIds, Collection<Long> groupIds) throws StorageException { + + var devices = storage.getObjects(Device.class, new Request( + new Columns.All(), + new Condition.Permission(User.class, userId, Device.class))); + var deviceById = devices.stream() + .collect(Collectors.toUnmodifiableMap(Device::getId, x -> x)); + var devicesByGroup = devices.stream() + .filter(x -> x.getGroupId() > 0) + .collect(Collectors.groupingBy(Device::getGroupId)); + + var groups = storage.getObjects(Group.class, new Request( + new Columns.All(), + new Condition.Permission(User.class, userId, Group.class))); + var groupsByGroup = groups.stream() + .filter(x -> x.getGroupId() > 0) + .collect(Collectors.groupingBy(Group::getGroupId)); + + var results = deviceIds.stream() + .map(deviceById::get) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + var groupQueue = new LinkedList<>(groupIds); + while (!groupQueue.isEmpty()) { + long groupId = groupQueue.pop(); + results.addAll(devicesByGroup.getOrDefault(groupId, Collections.emptyList())); + groupQueue.addAll(groupsByGroup.getOrDefault(groupId, Collections.emptyList()) + .stream().map(Group::getId).collect(Collectors.toUnmodifiableList())); + } + + return results; + } + +} diff --git a/src/main/java/org/traccar/helper/model/GeofenceUtil.java b/src/main/java/org/traccar/helper/model/GeofenceUtil.java new file mode 100644 index 000000000..9f063a8b4 --- /dev/null +++ b/src/main/java/org/traccar/helper/model/GeofenceUtil.java @@ -0,0 +1,42 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.helper.model; + +import org.traccar.config.Config; +import org.traccar.model.Geofence; +import org.traccar.model.Position; +import org.traccar.session.cache.CacheManager; + +import java.util.ArrayList; +import java.util.List; + +public final class GeofenceUtil { + + private GeofenceUtil() { + } + + public static List<Long> getCurrentGeofences(Config config, CacheManager cacheManager, Position position) { + List<Long> result = new ArrayList<>(); + for (Geofence geofence : cacheManager.getDeviceObjects(position.getDeviceId(), Geofence.class)) { + if (geofence.getGeometry().containsPoint( + config, geofence, position.getLatitude(), position.getLongitude())) { + result.add(geofence.getId()); + } + } + return result; + } + +} diff --git a/src/main/java/org/traccar/helper/model/PositionUtil.java b/src/main/java/org/traccar/helper/model/PositionUtil.java new file mode 100644 index 000000000..6c380b81a --- /dev/null +++ b/src/main/java/org/traccar/helper/model/PositionUtil.java @@ -0,0 +1,80 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.helper.model; + +import org.traccar.model.BaseModel; +import org.traccar.model.Device; +import org.traccar.model.Position; +import org.traccar.model.User; +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.Order; +import org.traccar.storage.query.Request; + +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +public final class PositionUtil { + + private PositionUtil() { + } + + public static boolean isLatest(CacheManager cacheManager, Position position) { + Position lastPosition = cacheManager.getPosition(position.getDeviceId()); + return lastPosition == null || position.getFixTime().compareTo(lastPosition.getFixTime()) >= 0; + } + + public static double calculateDistance(Position first, Position last, boolean useOdometer) { + double distance; + double firstOdometer = first.getDouble(Position.KEY_ODOMETER); + double lastOdometer = last.getDouble(Position.KEY_ODOMETER); + + if (useOdometer && firstOdometer != 0.0 && lastOdometer != 0.0) { + distance = lastOdometer - firstOdometer; + } else { + distance = last.getDouble(Position.KEY_TOTAL_DISTANCE) - first.getDouble(Position.KEY_TOTAL_DISTANCE); + } + return distance; + } + + public static List<Position> getPositions( + Storage storage, long deviceId, Date from, Date to) throws StorageException { + return storage.getObjects(Position.class, new Request( + new Columns.All(), + new Condition.And( + new Condition.Equals("deviceId", deviceId), + new Condition.Between("fixTime", "from", from, "to", to)), + new Order("fixTime"))); + } + + public static List<Position> getLatestPositions(Storage storage, long userId) throws StorageException { + var devices = storage.getObjects(Device.class, new Request( + new Columns.Include("id"), + new Condition.Permission(User.class, userId, Device.class))); + var deviceIds = devices.stream().map(BaseModel::getId).collect(Collectors.toUnmodifiableSet()); + + var positions = storage.getObjects(Position.class, new Request( + new Columns.All(), new Condition.LatestPositions())); + return positions.stream() + .filter(position -> deviceIds.contains(position.getDeviceId())) + .collect(Collectors.toList()); + } + +} diff --git a/src/main/java/org/traccar/helper/model/UserUtil.java b/src/main/java/org/traccar/helper/model/UserUtil.java new file mode 100644 index 000000000..4b1c404f9 --- /dev/null +++ b/src/main/java/org/traccar/helper/model/UserUtil.java @@ -0,0 +1,78 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.helper.model; + +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.model.Server; +import org.traccar.model.User; +import org.traccar.storage.Storage; +import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Order; +import org.traccar.storage.query.Request; + +import java.util.Date; +import java.util.TimeZone; + +public final class UserUtil { + + private UserUtil() { + } + + public static boolean isEmpty(Storage storage) throws StorageException { + return storage.getObjects(User.class, new Request( + new Columns.Include("id"), + new Order("id", false, 1))).isEmpty(); + } + + public static String getDistanceUnit(Server server, User user) { + return lookupStringAttribute(server, user, "distanceUnit", "km"); + } + + public static String getSpeedUnit(Server server, User user) { + return lookupStringAttribute(server, user, "speedUnit", "kn"); + } + + public static String getVolumeUnit(Server server, User user) { + return lookupStringAttribute(server, user, "volumeUnit", "ltr"); + } + + public static TimeZone getTimezone(Server server, User user) { + String timezone = lookupStringAttribute(server, user, "timezone", null); + return timezone != null ? TimeZone.getTimeZone(timezone) : TimeZone.getDefault(); + } + + private static String lookupStringAttribute(Server server, User user, String key, String defaultValue) { + String preference; + String serverPreference = server.getString(key); + String userPreference = user.getString(key); + if (server.getForceSettings()) { + preference = serverPreference != null ? serverPreference : userPreference; + } else { + preference = userPreference != null ? userPreference : serverPreference; + } + return preference != null ? preference : defaultValue; + } + + public static void setUserDefaults(User user, Config config) { + user.setDeviceLimit(config.getInteger(Keys.USERS_DEFAULT_DEVICE_LIMIT)); + int expirationDays = config.getInteger(Keys.USERS_DEFAULT_EXPIRATION_DAYS); + if (expirationDays > 0) { + user.setExpirationTime(new Date(System.currentTimeMillis() + expirationDays * 86400000L)); + } + } +} diff --git a/src/main/java/org/traccar/mail/LogMailManager.java b/src/main/java/org/traccar/mail/LogMailManager.java new file mode 100644 index 000000000..90de3bcce --- /dev/null +++ b/src/main/java/org/traccar/mail/LogMailManager.java @@ -0,0 +1,48 @@ +/* + * 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.mail; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.model.User; + +import jakarta.mail.MessagingException; +import jakarta.mail.internet.MimeBodyPart; + +public class LogMailManager implements MailManager { + + private static final Logger LOGGER = LoggerFactory.getLogger(LogMailManager.class); + + @Override + public boolean getEmailEnabled() { + return true; + } + + @Override + public void sendMessage( + User user, boolean system, String subject, String body) throws MessagingException { + sendMessage(user, system, subject, body, null); + } + + @Override + public void sendMessage( + User user, boolean system, String subject, String body, MimeBodyPart attachment) throws MessagingException { + LOGGER.info( + "Email sent\nTo: {}\nSubject: {}\nAttachment: {}\nBody:\n{}", + user.getEmail(), subject, attachment != null ? attachment.getFileName() : null, body); + } + +} diff --git a/src/main/java/org/traccar/mail/MailManager.java b/src/main/java/org/traccar/mail/MailManager.java new file mode 100644 index 000000000..d05a07de9 --- /dev/null +++ b/src/main/java/org/traccar/mail/MailManager.java @@ -0,0 +1,33 @@ +/* + * 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.mail; + +import org.traccar.model.User; + +import jakarta.mail.MessagingException; +import jakarta.mail.internet.MimeBodyPart; + +public interface MailManager { + + boolean getEmailEnabled(); + + void sendMessage( + User user, boolean system, String subject, String body) throws MessagingException; + + void sendMessage( + User user, boolean system, String subject, String body, MimeBodyPart attachment) throws MessagingException; + +} diff --git a/src/main/java/org/traccar/mail/SmtpMailManager.java b/src/main/java/org/traccar/mail/SmtpMailManager.java new file mode 100644 index 000000000..70099d879 --- /dev/null +++ b/src/main/java/org/traccar/mail/SmtpMailManager.java @@ -0,0 +1,162 @@ +/* + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2018 Andrey Kunitsyn (andrey@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.mail; + +import org.traccar.config.Config; +import org.traccar.config.ConfigKey; +import org.traccar.config.Keys; +import org.traccar.database.StatisticsManager; +import org.traccar.model.User; +import org.traccar.notification.PropertiesProvider; + +import jakarta.mail.BodyPart; +import jakarta.mail.Message; +import jakarta.mail.MessagingException; +import jakarta.mail.Multipart; +import jakarta.mail.Session; +import jakarta.mail.Transport; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeBodyPart; +import jakarta.mail.internet.MimeMessage; +import jakarta.mail.internet.MimeMultipart; +import java.io.UnsupportedEncodingException; +import java.util.Date; +import java.util.Properties; + +public final class SmtpMailManager implements MailManager { + + private static final String CONTENT_TYPE = "text/html; charset=utf-8"; + + private final Config config; + private final StatisticsManager statisticsManager; + + public SmtpMailManager(Config config, StatisticsManager statisticsManager) { + this.config = config; + this.statisticsManager = statisticsManager; + } + + private static void copyBooleanProperty( + Properties properties, PropertiesProvider provider, ConfigKey<Boolean> key) { + Boolean value = provider.getBoolean(key); + if (value != null) { + properties.put(key.getKey(), String.valueOf(value)); + } + } + + private static void copyStringProperty( + Properties properties, PropertiesProvider provider, ConfigKey<String> key) { + String value = provider.getString(key); + if (value != null) { + properties.put(key.getKey(), value); + } + } + + private static Properties getProperties(PropertiesProvider provider) { + String host = provider.getString(Keys.MAIL_SMTP_HOST); + if (host != null) { + Properties properties = new Properties(); + + properties.put(Keys.MAIL_TRANSPORT_PROTOCOL.getKey(), provider.getString(Keys.MAIL_TRANSPORT_PROTOCOL)); + properties.put(Keys.MAIL_SMTP_HOST.getKey(), host); + properties.put(Keys.MAIL_SMTP_PORT.getKey(), String.valueOf(provider.getInteger(Keys.MAIL_SMTP_PORT))); + + copyBooleanProperty(properties, provider, Keys.MAIL_SMTP_STARTTLS_ENABLE); + copyBooleanProperty(properties, provider, Keys.MAIL_SMTP_STARTTLS_REQUIRED); + copyBooleanProperty(properties, provider, Keys.MAIL_SMTP_SSL_ENABLE); + copyStringProperty(properties, provider, Keys.MAIL_SMTP_SSL_TRUST); + copyStringProperty(properties, provider, Keys.MAIL_SMTP_SSL_PROTOCOLS); + copyStringProperty(properties, provider, Keys.MAIL_SMTP_USERNAME); + copyStringProperty(properties, provider, Keys.MAIL_SMTP_PASSWORD); + copyStringProperty(properties, provider, Keys.MAIL_SMTP_FROM); + copyStringProperty(properties, provider, Keys.MAIL_SMTP_FROM_NAME); + + return properties; + } + return null; + } + + public boolean getEmailEnabled() { + return config.hasKey(Keys.MAIL_SMTP_HOST); + } + + @Override + public void sendMessage( + User user, boolean system, String subject, String body) throws MessagingException { + sendMessage(user, system, subject, body, null); + } + + @Override + public void sendMessage( + User user, boolean system, String subject, String body, MimeBodyPart attachment) throws MessagingException { + + Properties properties = null; + if (!config.getBoolean(Keys.MAIL_SMTP_IGNORE_USER_CONFIG)) { + properties = getProperties(new PropertiesProvider(user)); + } + if (properties == null && (system || !config.getBoolean(Keys.MAIL_SMTP_SYSTEM_ONLY))) { + properties = getProperties(new PropertiesProvider(config)); + } + if (properties == null) { + throw new MessagingException("No SMTP configuration found"); + } + + Session session = Session.getInstance(properties); + + MimeMessage message = new MimeMessage(session); + + String from = properties.getProperty(Keys.MAIL_SMTP_FROM.getKey()); + if (from != null) { + String fromName = properties.getProperty(Keys.MAIL_SMTP_FROM_NAME.getKey()); + if (fromName != null) { + try { + message.setFrom(new InternetAddress(from, fromName)); + } catch (UnsupportedEncodingException e) { + throw new MessagingException("Email address issue"); + } + } else { + message.setFrom(new InternetAddress(from)); + } + } + + message.addRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail())); + message.setSubject(subject); + message.setSentDate(new Date()); + + if (attachment != null) { + Multipart multipart = new MimeMultipart(); + + BodyPart messageBodyPart = new MimeBodyPart(); + messageBodyPart.setContent(body, CONTENT_TYPE); + multipart.addBodyPart(messageBodyPart); + multipart.addBodyPart(attachment); + + message.setContent(multipart); + } else { + message.setContent(body, CONTENT_TYPE); + } + + try (Transport transport = session.getTransport()) { + statisticsManager.registerMail(); + transport.connect( + properties.getProperty(Keys.MAIL_SMTP_HOST.getKey()), + properties.getProperty(Keys.MAIL_SMTP_USERNAME.getKey()), + properties.getProperty(Keys.MAIL_SMTP_PASSWORD.getKey())); + transport.sendMessage(message, message.getAllRecipients()); + } + } + +} diff --git a/src/main/java/org/traccar/database/CalendarManager.java b/src/main/java/org/traccar/model/BaseCommand.java index 44ced1082..f87b8ef65 100644 --- a/src/main/java/org/traccar/database/CalendarManager.java +++ b/src/main/java/org/traccar/model/BaseCommand.java @@ -1,6 +1,5 @@ /* - * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org) - * Copyright 2016 - 2017 Andrey Kunitsyn (andrey@traccar.org) + * Copyright 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,14 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.traccar.database; +package org.traccar.model; -import org.traccar.model.Calendar; +public class BaseCommand extends Message { -public class CalendarManager extends SimpleObjectManager<Calendar> { + private boolean textChannel; - public CalendarManager(DataManager dataManager) { - super(dataManager, Calendar.class); + public boolean getTextChannel() { + return textChannel; + } + + public void setTextChannel(boolean textChannel) { + this.textChannel = textChannel; } } diff --git a/src/main/java/org/traccar/model/BaseModel.java b/src/main/java/org/traccar/model/BaseModel.java index 8bdb916e8..acde0f83d 100644 --- a/src/main/java/org/traccar/model/BaseModel.java +++ b/src/main/java/org/traccar/model/BaseModel.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org) * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,11 +20,11 @@ public class BaseModel { private long id; - public final long getId() { + public long getId() { return id; } - public final void setId(long id) { + public void setId(long id) { this.id = id; } diff --git a/src/main/java/org/traccar/model/Calendar.java b/src/main/java/org/traccar/model/Calendar.java index 102c0be52..03f1995ba 100644 --- a/src/main/java/org/traccar/model/Calendar.java +++ b/src/main/java/org/traccar/model/Calendar.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,6 +24,7 @@ import net.fortuna.ical4j.filter.predicate.PeriodRule; import net.fortuna.ical4j.model.DateTime; import net.fortuna.ical4j.model.Period; import net.fortuna.ical4j.model.component.CalendarComponent; +import net.fortuna.ical4j.model.component.VEvent; import org.traccar.storage.QueryIgnore; import org.traccar.storage.StorageName; @@ -32,6 +33,8 @@ import java.io.IOException; import java.time.Duration; import java.util.Collection; import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; @StorageName("tc_calendars") public class Calendar extends ExtendedModel { @@ -49,13 +52,13 @@ public class Calendar extends ExtendedModel { private byte[] data; public byte[] getData() { - return data.clone(); + return data; } public void setData(byte[] data) throws IOException, ParserException { CalendarBuilder builder = new CalendarBuilder(); calendar = builder.build(new ByteArrayInputStream(data)); - this.data = data.clone(); + this.data = data; } private net.fortuna.ical4j.model.Calendar calendar; @@ -66,14 +69,24 @@ public class Calendar extends ExtendedModel { return calendar; } - public boolean checkMoment(Date date) { + private Collection<VEvent> findEvents(Date date) { if (calendar != null) { - Period period = new Period(new DateTime(date), Duration.ZERO); - Filter<CalendarComponent> filter = new Filter<>(new PeriodRule<>(period)); - Collection<CalendarComponent> events = filter.filter(calendar.getComponents(CalendarComponent.VEVENT)); - return events != null && !events.isEmpty(); + var filter = new Filter<VEvent>(new PeriodRule<>(new Period(new DateTime(date), Duration.ZERO))); + return filter.filter(calendar.getComponents(CalendarComponent.VEVENT)); + } else { + return List.of(); } - return false; + } + + public Collection<Period> findPeriods(Date date) { + var calendarDate = new net.fortuna.ical4j.model.Date(date); + return findEvents(date).stream() + .flatMap((event) -> event.getConsumedTime(calendarDate, calendarDate).stream()) + .collect(Collectors.toSet()); + } + + public boolean checkMoment(Date date) { + return !findEvents(date).isEmpty(); } } diff --git a/src/main/java/org/traccar/model/CellTower.java b/src/main/java/org/traccar/model/CellTower.java index 254487471..355594c64 100644 --- a/src/main/java/org/traccar/model/CellTower.java +++ b/src/main/java/org/traccar/model/CellTower.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,9 +16,11 @@ package org.traccar.model; import com.fasterxml.jackson.annotation.JsonInclude; -import org.traccar.Context; +import org.traccar.config.Config; import org.traccar.config.Keys; +import java.util.Objects; + @JsonInclude(JsonInclude.Include.NON_NULL) public class CellTower { @@ -37,14 +39,12 @@ public class CellTower { return cellTower; } - public static CellTower fromLacCid(int lac, long cid) { - return from( - Context.getConfig().getInteger(Keys.GEOLOCATION_MCC), - Context.getConfig().getInteger(Keys.GEOLOCATION_MCC), lac, cid); + public static CellTower fromLacCid(Config config, int lac, long cid) { + return from(config.getInteger(Keys.GEOLOCATION_MCC), config.getInteger(Keys.GEOLOCATION_MNC), lac, cid); } - public static CellTower fromCidLac(long cid, int lac) { - return fromLacCid(lac, cid); + public static CellTower fromCidLac(Config config, long cid, int lac) { + return fromLacCid(config, lac, cid); } private String radioType; @@ -113,4 +113,26 @@ public class CellTower { mobileNetworkCode = Integer.parseInt(operatorString.substring(3)); } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CellTower cellTower = (CellTower) o; + return Objects.equals(radioType, cellTower.radioType) + && Objects.equals(cellId, cellTower.cellId) + && Objects.equals(locationAreaCode, cellTower.locationAreaCode) + && Objects.equals(mobileCountryCode, cellTower.mobileCountryCode) + && Objects.equals(mobileNetworkCode, cellTower.mobileNetworkCode) + && Objects.equals(signalStrength, cellTower.signalStrength); + } + + @Override + public int hashCode() { + return Objects.hash(radioType, cellId, locationAreaCode, mobileCountryCode, mobileNetworkCode, signalStrength); + } + } diff --git a/src/main/java/org/traccar/model/Command.java b/src/main/java/org/traccar/model/Command.java index 03961c7b2..99988dd82 100644 --- a/src/main/java/org/traccar/model/Command.java +++ b/src/main/java/org/traccar/model/Command.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org) + * 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. @@ -22,7 +22,7 @@ import org.traccar.storage.StorageName; @StorageName("tc_commands") @JsonIgnoreProperties(ignoreUnknown = true) -public class Command extends Message implements Cloneable { +public class Command extends BaseCommand { public static final String TYPE_CUSTOM = "custom"; public static final String TYPE_IDENTIFICATION = "deviceIdentification"; @@ -58,11 +58,10 @@ public class Command extends Message implements Cloneable { public static final String TYPE_GET_MODEM_STATUS = "getModemStatus"; public static final String TYPE_GET_DEVICE_STATUS = "getDeviceStatus"; public static final String TYPE_SET_SPEED_LIMIT = "setSpeedLimit"; - public static final String TYPE_MODE_POWER_SAVING = "modePowerSaving"; public static final String TYPE_MODE_DEEP_SLEEP = "modeDeepSleep"; - public static final String TYPE_ALARM_GEOFENCE = "movementAlarm"; + public static final String TYPE_ALARM_GEOFENCE = "alarmGeofence"; public static final String TYPE_ALARM_BATTERY = "alarmBattery"; public static final String TYPE_ALARM_SOS = "alarmSos"; public static final String TYPE_ALARM_REMOVE = "alarmRemove"; @@ -85,21 +84,6 @@ public class Command extends Message implements Cloneable { public static final String KEY_SERVER = "server"; public static final String KEY_PORT = "port"; - @Override - public Command clone() throws CloneNotSupportedException { - return (Command) super.clone(); - } - - private boolean textChannel; - - public boolean getTextChannel() { - return textChannel; - } - - public void setTextChannel(boolean textChannel) { - this.textChannel = textChannel; - } - @QueryIgnore @Override public long getDeviceId() { diff --git a/src/main/java/org/traccar/model/Device.java b/src/main/java/org/traccar/model/Device.java index 219f078ed..e07815976 100644 --- a/src/main/java/org/traccar/model/Device.java +++ b/src/main/java/org/traccar/model/Device.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 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. @@ -15,15 +15,26 @@ */ package org.traccar.model; -import java.util.Date; -import java.util.List; - -import org.traccar.storage.QueryExtended; +import com.fasterxml.jackson.annotation.JsonIgnore; import org.traccar.storage.QueryIgnore; import org.traccar.storage.StorageName; +import java.util.Date; + @StorageName("tc_devices") -public class Device extends GroupedModel { +public class Device extends GroupedModel implements Disableable, Schedulable { + + private long calendarId; + + @Override + public long getCalendarId() { + return calendarId; + } + + @Override + public void setCalendarId(long calendarId) { + this.calendarId = calendarId; + } private String name; @@ -42,7 +53,7 @@ public class Device extends GroupedModel { } public void setUniqueId(String uniqueId) { - this.uniqueId = uniqueId; + this.uniqueId = uniqueId.trim(); } public static final String STATUS_UNKNOWN = "unknown"; @@ -56,18 +67,17 @@ public class Device extends GroupedModel { return status != null ? status : STATUS_OFFLINE; } - @QueryIgnore public void setStatus(String status) { - this.status = status; + this.status = status != null ? status.trim() : null; } private Date lastUpdate; + @QueryIgnore public Date getLastUpdate() { return this.lastUpdate; } - @QueryExtended public void setLastUpdate(Date lastUpdate) { this.lastUpdate = lastUpdate; } @@ -79,23 +89,10 @@ public class Device extends GroupedModel { return positionId; } - @QueryIgnore public void setPositionId(long positionId) { this.positionId = positionId; } - private List<Long> geofenceIds; - - @QueryIgnore - public List<Long> getGeofenceIds() { - return geofenceIds; - } - - @QueryIgnore - public void setGeofenceIds(List<Long> geofenceIds) { - this.geofenceIds = geofenceIds; - } - private String phone; public String getPhone() { @@ -103,7 +100,7 @@ public class Device extends GroupedModel { } public void setPhone(String phone) { - this.phone = phone; + this.phone = phone != null ? phone.trim() : null; } private String model; @@ -138,12 +135,117 @@ public class Device extends GroupedModel { private boolean disabled; + @Override public boolean getDisabled() { return disabled; } + @Override public void setDisabled(boolean disabled) { this.disabled = disabled; } + private Date expirationTime; + + @Override + public Date getExpirationTime() { + return expirationTime; + } + + @Override + public void setExpirationTime(Date expirationTime) { + this.expirationTime = expirationTime; + } + + private boolean motionStreak; + + @QueryIgnore + @JsonIgnore + public boolean getMotionStreak() { + return motionStreak; + } + + @JsonIgnore + public void setMotionStreak(boolean motionStreak) { + this.motionStreak = motionStreak; + } + + private boolean motionState; + + @QueryIgnore + @JsonIgnore + public boolean getMotionState() { + return motionState; + } + + @JsonIgnore + public void setMotionState(boolean motionState) { + this.motionState = motionState; + } + + private Date motionTime; + + @QueryIgnore + @JsonIgnore + public Date getMotionTime() { + return motionTime; + } + + @JsonIgnore + public void setMotionTime(Date motionTime) { + this.motionTime = motionTime; + } + + private double motionDistance; + + @QueryIgnore + @JsonIgnore + public double getMotionDistance() { + return motionDistance; + } + + @JsonIgnore + public void setMotionDistance(double motionDistance) { + this.motionDistance = motionDistance; + } + + private boolean overspeedState; + + @QueryIgnore + @JsonIgnore + public boolean getOverspeedState() { + return overspeedState; + } + + @JsonIgnore + public void setOverspeedState(boolean overspeedState) { + this.overspeedState = overspeedState; + } + + private Date overspeedTime; + + @QueryIgnore + @JsonIgnore + public Date getOverspeedTime() { + return overspeedTime; + } + + @JsonIgnore + public void setOverspeedTime(Date overspeedTime) { + this.overspeedTime = overspeedTime; + } + + private long overspeedGeofenceId; + + @QueryIgnore + @JsonIgnore + public long getOverspeedGeofenceId() { + return overspeedGeofenceId; + } + + @JsonIgnore + public void setOverspeedGeofenceId(long overspeedGeofenceId) { + this.overspeedGeofenceId = overspeedGeofenceId; + } + } diff --git a/src/main/java/org/traccar/model/DeviceState.java b/src/main/java/org/traccar/model/DeviceState.java deleted file mode 100644 index 75d6726ee..000000000 --- a/src/main/java/org/traccar/model/DeviceState.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2017 Anton Tananaev (anton@traccar.org) - * Copyright 2017 Andrey Kunitsyn (andrey@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.model; - -public class DeviceState { - - private Boolean motionState; - - public void setMotionState(boolean motionState) { - this.motionState = motionState; - } - - public Boolean getMotionState() { - return motionState; - } - - private Position motionPosition; - - public void setMotionPosition(Position motionPosition) { - this.motionPosition = motionPosition; - } - - public Position getMotionPosition() { - return motionPosition; - } - - private Boolean overspeedState; - - public void setOverspeedState(boolean overspeedState) { - this.overspeedState = overspeedState; - } - - public Boolean getOverspeedState() { - return overspeedState; - } - - private Position overspeedPosition; - - public void setOverspeedPosition(Position overspeedPosition) { - this.overspeedPosition = overspeedPosition; - } - - public Position getOverspeedPosition() { - return overspeedPosition; - } - - private long overspeedGeofenceId; - - public void setOverspeedGeofenceId(long overspeedGeofenceId) { - this.overspeedGeofenceId = overspeedGeofenceId; - } - - public long getOverspeedGeofenceId() { - return overspeedGeofenceId; - } - -} diff --git a/src/main/java/org/traccar/model/Disableable.java b/src/main/java/org/traccar/model/Disableable.java new file mode 100644 index 000000000..1145d6279 --- /dev/null +++ b/src/main/java/org/traccar/model/Disableable.java @@ -0,0 +1,39 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.model; + +import java.util.Date; + +public interface Disableable { + + boolean getDisabled(); + + void setDisabled(boolean disabled); + + Date getExpirationTime(); + + void setExpirationTime(Date expirationTime); + + default void checkDisabled() throws SecurityException { + if (getDisabled()) { + throw new SecurityException(getClass().getSimpleName() + " is disabled"); + } + if (getExpirationTime() != null && System.currentTimeMillis() > getExpirationTime().getTime()) { + throw new SecurityException(getClass().getSimpleName() + " has expired"); + } + } + +} diff --git a/src/main/java/org/traccar/model/Driver.java b/src/main/java/org/traccar/model/Driver.java index b9e023088..ca5714e51 100644 --- a/src/main/java/org/traccar/model/Driver.java +++ b/src/main/java/org/traccar/model/Driver.java @@ -38,7 +38,7 @@ public class Driver extends ExtendedModel { } public void setUniqueId(String uniqueId) { - this.uniqueId = uniqueId; + this.uniqueId = uniqueId.trim(); } } diff --git a/src/main/java/org/traccar/model/Event.java b/src/main/java/org/traccar/model/Event.java index 6e3953fda..0e851d748 100644 --- a/src/main/java/org/traccar/model/Event.java +++ b/src/main/java/org/traccar/model/Event.java @@ -52,6 +52,7 @@ public class Event extends Message { public static final String TYPE_DEVICE_OVERSPEED = "deviceOverspeed"; public static final String TYPE_DEVICE_FUEL_DROP = "deviceFuelDrop"; + public static final String TYPE_DEVICE_FUEL_INCREASE = "deviceFuelIncrease"; public static final String TYPE_GEOFENCE_ENTER = "geofenceEnter"; public static final String TYPE_GEOFENCE_EXIT = "geofenceExit"; @@ -62,10 +63,9 @@ public class Event extends Message { public static final String TYPE_IGNITION_OFF = "ignitionOff"; public static final String TYPE_MAINTENANCE = "maintenance"; - public static final String TYPE_TEXT_MESSAGE = "textMessage"; - public static final String TYPE_DRIVER_CHANGED = "driverChanged"; + public static final String TYPE_MEDIA = "media"; private Date eventTime; diff --git a/src/main/java/org/traccar/model/ExtendedModel.java b/src/main/java/org/traccar/model/ExtendedModel.java index 8353d0e66..d5cd094da 100644 --- a/src/main/java/org/traccar/model/ExtendedModel.java +++ b/src/main/java/org/traccar/model/ExtendedModel.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,17 +17,22 @@ package org.traccar.model; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Objects; public class ExtendedModel extends BaseModel { private Map<String, Object> attributes = new LinkedHashMap<>(); + public boolean hasAttribute(String key) { + return attributes.containsKey(key); + } + public Map<String, Object> getAttributes() { return attributes; } public void setAttributes(Map<String, Object> attributes) { - this.attributes = attributes; + this.attributes = Objects.requireNonNullElseGet(attributes, LinkedHashMap::new); } public void set(String key, Boolean value) { @@ -84,17 +89,27 @@ public class ExtendedModel extends BaseModel { } } - public String getString(String key) { + public String getString(String key, String defaultValue) { if (attributes.containsKey(key)) { - return (String) attributes.get(key); + Object value = attributes.get(key); + return value != null ? value.toString() : null; } else { - return null; + return defaultValue; } } + public String getString(String key) { + return getString(key, null); + } + public double getDouble(String key) { if (attributes.containsKey(key)) { - return ((Number) attributes.get(key)).doubleValue(); + Object value = attributes.get(key); + if (value instanceof Number) { + return ((Number) attributes.get(key)).doubleValue(); + } else { + return Double.parseDouble(value.toString()); + } } else { return 0.0; } @@ -102,7 +117,12 @@ public class ExtendedModel extends BaseModel { public boolean getBoolean(String key) { if (attributes.containsKey(key)) { - return (Boolean) attributes.get(key); + Object value = attributes.get(key); + if (value instanceof Boolean) { + return (Boolean) attributes.get(key); + } else { + return Boolean.parseBoolean(value.toString()); + } } else { return false; } @@ -110,7 +130,12 @@ public class ExtendedModel extends BaseModel { public int getInteger(String key) { if (attributes.containsKey(key)) { - return ((Number) attributes.get(key)).intValue(); + Object value = attributes.get(key); + if (value instanceof Number) { + return ((Number) attributes.get(key)).intValue(); + } else { + return Integer.parseInt(value.toString()); + } } else { return 0; } @@ -118,7 +143,12 @@ public class ExtendedModel extends BaseModel { public long getLong(String key) { if (attributes.containsKey(key)) { - return ((Number) attributes.get(key)).longValue(); + Object value = attributes.get(key); + if (value instanceof Number) { + return ((Number) attributes.get(key)).longValue(); + } else { + return Long.parseLong(value.toString()); + } } else { return 0; } diff --git a/src/main/java/org/traccar/model/Geofence.java b/src/main/java/org/traccar/model/Geofence.java index 5b857580d..ca6293651 100644 --- a/src/main/java/org/traccar/model/Geofence.java +++ b/src/main/java/org/traccar/model/Geofence.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 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. @@ -15,25 +15,30 @@ */ package org.traccar.model; -import java.text.ParseException; - -import org.traccar.Context; -import org.traccar.config.Keys; -import org.traccar.storage.QueryIgnore; +import com.fasterxml.jackson.annotation.JsonIgnore; import org.traccar.geofence.GeofenceCircle; import org.traccar.geofence.GeofenceGeometry; import org.traccar.geofence.GeofencePolygon; import org.traccar.geofence.GeofencePolyline; - -import com.fasterxml.jackson.annotation.JsonIgnore; +import org.traccar.storage.QueryIgnore; import org.traccar.storage.StorageName; +import java.text.ParseException; + @StorageName("tc_geofences") -public class Geofence extends ScheduledModel { +public class Geofence extends ExtendedModel implements Schedulable { - public static final String TYPE_GEOFENCE_CILCLE = "geofenceCircle"; - public static final String TYPE_GEOFENCE_POLYGON = "geofencePolygon"; - public static final String TYPE_GEOFENCE_POLYLINE = "geofencePolyline"; + private long calendarId; + + @Override + public long getCalendarId() { + return calendarId; + } + + @Override + public void setCalendarId(long calendarId) { + this.calendarId = calendarId; + } private String name; @@ -68,9 +73,7 @@ public class Geofence extends ScheduledModel { } else if (area.startsWith("POLYGON")) { geometry = new GeofencePolygon(area); } else if (area.startsWith("LINESTRING")) { - final double distance = getDouble("polylineDistance"); - geometry = new GeofencePolyline(area, distance > 0 ? distance - : Context.getConfig().getDouble(Keys.GEOFENCE_POLYLINE_DISTANCE)); + geometry = new GeofencePolyline(area); } else { throw new ParseException("Unknown geometry type", 0); } @@ -92,4 +95,5 @@ public class Geofence extends ScheduledModel { area = geometry.toWkt(); this.geometry = geometry; } + } diff --git a/src/main/java/org/traccar/model/Network.java b/src/main/java/org/traccar/model/Network.java index 4d67fc5d8..b31c53c38 100644 --- a/src/main/java/org/traccar/model/Network.java +++ b/src/main/java/org/traccar/model/Network.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import java.util.ArrayList; import java.util.Collection; +import java.util.Objects; @JsonInclude(JsonInclude.Include.NON_NULL) public class Network { @@ -118,4 +119,27 @@ public class Network { wifiAccessPoints.add(wifiAccessPoint); } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Network network = (Network) o; + return Objects.equals(homeMobileCountryCode, network.homeMobileCountryCode) + && Objects.equals(homeMobileNetworkCode, network.homeMobileNetworkCode) + && Objects.equals(radioType, network.radioType) + && Objects.equals(carrier, network.carrier) + && Objects.equals(cellTowers, network.cellTowers) + && Objects.equals(wifiAccessPoints, network.wifiAccessPoints); + } + + @Override + public int hashCode() { + return Objects.hash( + homeMobileCountryCode, homeMobileNetworkCode, radioType, carrier, cellTowers, wifiAccessPoints); + } + } diff --git a/src/main/java/org/traccar/model/Notification.java b/src/main/java/org/traccar/model/Notification.java index 95e446132..6dcd9c9de 100644 --- a/src/main/java/org/traccar/model/Notification.java +++ b/src/main/java/org/traccar/model/Notification.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 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. @@ -24,7 +24,19 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import org.traccar.storage.StorageName; @StorageName("tc_notifications") -public class Notification extends ScheduledModel { +public class Notification extends ExtendedModel implements Schedulable { + + private long calendarId; + + @Override + public long getCalendarId() { + return calendarId; + } + + @Override + public void setCalendarId(long calendarId) { + this.calendarId = calendarId; + } private boolean always; @@ -46,6 +58,16 @@ public class Notification extends ScheduledModel { this.type = type; } + private long commandId; + + public long getCommandId() { + return commandId; + } + + public void setCommandId(long commandId) { + this.commandId = commandId; + } + private String notificators; public String getNotificators() { diff --git a/src/main/java/org/traccar/model/Permission.java b/src/main/java/org/traccar/model/Permission.java index ad0176b39..0b2f0584f 100644 --- a/src/main/java/org/traccar/model/Permission.java +++ b/src/main/java/org/traccar/model/Permission.java @@ -31,12 +31,12 @@ import org.traccar.storage.QueryIgnore; public class Permission { - private static final Map<String, Class<?>> CLASSES = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + private static final Map<String, Class<? extends BaseModel>> CLASSES = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); static { try { for (Class<?> clazz : ClassScanner.findSubclasses(BaseModel.class)) { - CLASSES.put(clazz.getSimpleName(), clazz); + CLASSES.put(clazz.getSimpleName(), (Class<? extends BaseModel>) clazz); } } catch (IOException | ReflectiveOperationException | URISyntaxException e) { throw new RuntimeException(e); @@ -45,23 +45,25 @@ public class Permission { private final LinkedHashMap<String, Long> data; - private final Class<?> ownerClass; + private final Class<? extends BaseModel> ownerClass; private final long ownerId; - private final Class<?> propertyClass; + private final Class<? extends BaseModel> propertyClass; private final long propertyId; public Permission(LinkedHashMap<String, Long> data) { this.data = data; var iterator = data.entrySet().iterator(); var owner = iterator.next(); - ownerClass = CLASSES.get(owner.getKey().substring(0, owner.getKey().length() - 2)); + ownerClass = getKeyClass(owner.getKey()); ownerId = owner.getValue(); var property = iterator.next(); - propertyClass = CLASSES.get(property.getKey().substring(0, property.getKey().length() - 2)); + propertyClass = getKeyClass(property.getKey()); propertyId = property.getValue(); } - public Permission(Class<?> ownerClass, long ownerId, Class<?> propertyClass, long propertyId) { + public Permission( + Class<? extends BaseModel> ownerClass, long ownerId, + Class<? extends BaseModel> propertyClass, long propertyId) { this.ownerClass = ownerClass; this.ownerId = ownerId; this.propertyClass = propertyClass; @@ -71,7 +73,11 @@ public class Permission { data.put(getKey(propertyClass), propertyId); } - private static String getKey(Class<?> clazz) { + public static Class<? extends BaseModel> getKeyClass(String key) { + return CLASSES.get(key.substring(0, key.length() - 2)); + } + + public static String getKey(Class<?> clazz) { return Introspector.decapitalize(clazz.getSimpleName()) + "Id"; } @@ -105,7 +111,7 @@ public class Permission { @QueryIgnore @JsonIgnore - public Class<?> getOwnerClass() { + public Class<? extends BaseModel> getOwnerClass() { return ownerClass; } @@ -117,7 +123,7 @@ public class Permission { @QueryIgnore @JsonIgnore - public Class<?> getPropertyClass() { + public Class<? extends BaseModel> getPropertyClass() { return propertyClass; } diff --git a/src/main/java/org/traccar/model/Position.java b/src/main/java/org/traccar/model/Position.java index 348370e2c..6685cab95 100644 --- a/src/main/java/org/traccar/model/Position.java +++ b/src/main/java/org/traccar/model/Position.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ package org.traccar.model; import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; import com.fasterxml.jackson.annotation.JsonIgnore; import org.traccar.storage.QueryIgnore; @@ -40,7 +42,7 @@ public class Position extends Message { public static final String KEY_ODOMETER = "odometer"; // meters public static final String KEY_ODOMETER_SERVICE = "serviceOdometer"; // meters public static final String KEY_ODOMETER_TRIP = "tripOdometer"; // meters - public static final String KEY_HOURS = "hours"; + 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_INPUT = "input"; @@ -83,12 +85,14 @@ public class Position extends Message { public static final String KEY_OPERATOR = "operator"; public static final String KEY_COMMAND = "command"; public static final String KEY_BLOCKED = "blocked"; + public static final String KEY_LOCK = "lock"; public static final String KEY_DOOR = "door"; public static final String KEY_AXLE_WEIGHT = "axleWeight"; public static final String KEY_G_SENSOR = "gSensor"; public static final String KEY_ICCID = "iccid"; public static final String KEY_PHONE = "phone"; public static final String KEY_SPEED_LIMIT = "speedLimit"; + public static final String KEY_DRIVING_TIME = "drivingTime"; public static final String KEY_DTCS = "dtcs"; public static final String KEY_OBD_SPEED = "obdSpeed"; // knots @@ -97,6 +101,7 @@ public class Position extends Message { public static final String KEY_RESULT = "result"; public static final String KEY_DRIVER_UNIQUE_ID = "driverUniqueId"; + public static final String KEY_CARD = "card"; // Start with 1 not 0 public static final String PREFIX_TEMP = "temp"; @@ -150,7 +155,6 @@ public class Position extends Message { public Position(String protocol) { this.protocol = protocol; - this.serverTime = new Date(); } private String protocol; @@ -228,6 +232,9 @@ public class Position extends Message { } public void setLatitude(double latitude) { + if (latitude < -90 || latitude > 90) { + throw new IllegalArgumentException("Latitude out of range"); + } this.latitude = latitude; } @@ -238,6 +245,9 @@ public class Position extends Message { } public void setLongitude(double longitude) { + if (longitude < -180 || longitude > 180) { + throw new IllegalArgumentException("Longitude out of range"); + } this.longitude = longitude; } @@ -301,6 +311,20 @@ public class Position extends Message { this.network = network; } + private List<Long> geofenceIds; + + public List<Long> getGeofenceIds() { + return geofenceIds; + } + + public void setGeofenceIds(List<? extends Number> geofenceIds) { + if (geofenceIds != null) { + this.geofenceIds = geofenceIds.stream().map(Number::longValue).collect(Collectors.toList()); + } else { + this.geofenceIds = null; + } + } + @JsonIgnore @QueryIgnore @Override diff --git a/src/main/java/org/traccar/model/QueuedCommand.java b/src/main/java/org/traccar/model/QueuedCommand.java new file mode 100644 index 000000000..96a1eca4b --- /dev/null +++ b/src/main/java/org/traccar/model/QueuedCommand.java @@ -0,0 +1,46 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import org.traccar.storage.StorageName; + +import java.util.HashMap; + +@StorageName("tc_commands_queue") +@JsonIgnoreProperties(ignoreUnknown = true) +public class QueuedCommand extends BaseCommand { + + public static QueuedCommand fromCommand(Command command) { + QueuedCommand queuedCommand = new QueuedCommand(); + queuedCommand.setDeviceId(command.getDeviceId()); + queuedCommand.setType(command.getType()); + queuedCommand.setTextChannel(command.getTextChannel()); + queuedCommand.setAttributes(new HashMap<>(command.getAttributes())); + return queuedCommand; + } + + public Command toCommand() { + Command command = new Command(); + command.setDeviceId(getDeviceId()); + command.setType(getType()); + command.setDescription(""); + command.setTextChannel(getTextChannel()); + command.setAttributes(new HashMap<>(getAttributes())); + return command; + } + +} diff --git a/src/main/java/org/traccar/model/Report.java b/src/main/java/org/traccar/model/Report.java new file mode 100644 index 000000000..2ee7ae288 --- /dev/null +++ b/src/main/java/org/traccar/model/Report.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.model; + +import org.traccar.storage.StorageName; + +@StorageName("tc_reports") +public class Report extends ExtendedModel implements Schedulable { + + private long calendarId; + + @Override + public long getCalendarId() { + return calendarId; + } + + @Override + public void setCalendarId(long calendarId) { + this.calendarId = calendarId; + } + + private String type; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + private String description; + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + +} diff --git a/src/main/java/org/traccar/model/ScheduledModel.java b/src/main/java/org/traccar/model/Schedulable.java index 9e6a4b9a6..331e77583 100644 --- a/src/main/java/org/traccar/model/ScheduledModel.java +++ b/src/main/java/org/traccar/model/Schedulable.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2018 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,15 +16,7 @@ */ package org.traccar.model; -public class ScheduledModel extends ExtendedModel { - - private long calendarId; - - public long getCalendarId() { - return calendarId; - } - - public void setCalendarId(long calendarId) { - this.calendarId = calendarId; - } +public interface Schedulable { + long getCalendarId(); + void setCalendarId(long calendarId); } diff --git a/src/main/java/org/traccar/model/Server.java b/src/main/java/org/traccar/model/Server.java index b48e84939..6442186b6 100644 --- a/src/main/java/org/traccar/model/Server.java +++ b/src/main/java/org/traccar/model/Server.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 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. @@ -16,13 +16,13 @@ package org.traccar.model; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import org.traccar.Context; + import org.traccar.storage.QueryIgnore; import org.traccar.storage.StorageName; @StorageName("tc_servers") @JsonIgnoreProperties(ignoreUnknown = true) -public class Server extends ExtendedModel { +public class Server extends ExtendedModel implements UserRestrictions { private boolean registration; @@ -36,6 +36,7 @@ public class Server extends ExtendedModel { private boolean readonly; + @Override public boolean getReadonly() { return readonly; } @@ -46,6 +47,7 @@ public class Server extends ExtendedModel { private boolean deviceReadonly; + @Override public boolean getDeviceReadonly() { return deviceReadonly; } @@ -84,6 +86,16 @@ public class Server extends ExtendedModel { this.mapUrl = mapUrl; } + private String overlayUrl; + + public String getOverlayUrl() { + return overlayUrl; + } + + public void setOverlayUrl(String overlayUrl) { + this.overlayUrl = overlayUrl; + } + private double latitude; public double getLatitude() { @@ -146,6 +158,7 @@ public class Server extends ExtendedModel { private boolean limitCommands; + @Override public boolean getLimitCommands() { return limitCommands; } @@ -156,6 +169,7 @@ public class Server extends ExtendedModel { private boolean disableReports; + @Override public boolean getDisableReports() { return disableReports; } @@ -164,6 +178,17 @@ public class Server extends ExtendedModel { this.disableReports = disableReports; } + private boolean fixedEmail; + + @Override + public boolean getFixedEmail() { + return fixedEmail; + } + + public void setFixedEmail(boolean fixedEmail) { + this.fixedEmail = fixedEmail; + } + private String poiLayer; public String getPoiLayer() { @@ -189,9 +214,87 @@ public class Server extends ExtendedModel { return getClass().getPackage().getImplementationVersion(); } + private boolean emailEnabled; + + @QueryIgnore + public void setEmailEnabled(boolean emailEnabled) { + this.emailEnabled = emailEnabled; + } + @QueryIgnore public Boolean getEmailEnabled() { - return Context.getMailManager().getEmailEnabled(); + return emailEnabled; + } + + private boolean geocoderEnabled; + + private boolean textEnabled; + + @QueryIgnore + public void setTextEnabled(boolean textEnabled) { + this.textEnabled = textEnabled; + } + + @QueryIgnore + public Boolean getTextEnabled() { + return textEnabled; + } + + @QueryIgnore + public void setGeocoderEnabled(boolean geocoderEnabled) { + this.geocoderEnabled = geocoderEnabled; + } + + @QueryIgnore + public boolean getGeocoderEnabled() { + return geocoderEnabled; + } + + private long[] storageSpace; + + @QueryIgnore + public long[] getStorageSpace() { + return storageSpace; + } + + @QueryIgnore + public void setStorageSpace(long[] storageSpace) { + this.storageSpace = storageSpace; + } + + private boolean newServer; + + @QueryIgnore + public boolean getNewServer() { + return newServer; + } + + @QueryIgnore + public void setNewServer(boolean newServer) { + this.newServer = newServer; + } + + private boolean openIdEnabled; + + @QueryIgnore + public boolean getOpenIdEnabled() { + return openIdEnabled; + } + + @QueryIgnore + public void setOpenIdEnabled(boolean openIdEnabled) { + this.openIdEnabled = openIdEnabled; } + private boolean openIdForce; + + @QueryIgnore + public boolean getOpenIdForce() { + return openIdForce; + } + + @QueryIgnore + public void setOpenIdForce(boolean openIdForce) { + this.openIdForce = openIdForce; + } } diff --git a/src/main/java/org/traccar/model/User.java b/src/main/java/org/traccar/model/User.java index 464d0cbfe..0540f16d7 100644 --- a/src/main/java/org/traccar/model/User.java +++ b/src/main/java/org/traccar/model/User.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,6 @@ package org.traccar.model; import com.fasterxml.jackson.annotation.JsonIgnore; -import org.traccar.storage.QueryExtended; import org.traccar.storage.QueryIgnore; import org.traccar.helper.Hashing; import org.traccar.storage.StorageName; @@ -25,7 +24,7 @@ import org.traccar.storage.StorageName; import java.util.Date; @StorageName("tc_users") -public class User extends ExtendedModel { +public class User extends ExtendedModel implements UserRestrictions, Disableable { private String name; @@ -64,11 +63,12 @@ public class User extends ExtendedModel { } public void setPhone(String phone) { - this.phone = phone; + this.phone = phone != null ? phone.trim() : null; } private boolean readonly; + @Override public boolean getReadonly() { return readonly; } @@ -79,6 +79,12 @@ public class User extends ExtendedModel { private boolean administrator; + @QueryIgnore + @JsonIgnore + public boolean getManager() { + return userLimit != 0; + } + public boolean getAdministrator() { return administrator; } @@ -149,20 +155,24 @@ public class User extends ExtendedModel { private boolean disabled; + @Override public boolean getDisabled() { return disabled; } + @Override public void setDisabled(boolean disabled) { this.disabled = disabled; } private Date expirationTime; + @Override public Date getExpirationTime() { return expirationTime; } + @Override public void setExpirationTime(Date expirationTime) { this.expirationTime = expirationTime; } @@ -189,6 +199,7 @@ public class User extends ExtendedModel { private boolean deviceReadonly; + @Override public boolean getDeviceReadonly() { return deviceReadonly; } @@ -197,25 +208,9 @@ public class User extends ExtendedModel { this.deviceReadonly = deviceReadonly; } - private String token; - - public String getToken() { - return token; - } - - public void setToken(String token) { - if (token != null && !token.isEmpty()) { - if (!token.matches("^[a-zA-Z0-9-]{16,}$")) { - throw new IllegalArgumentException("Illegal token"); - } - this.token = token; - } else { - this.token = null; - } - } - private boolean limitCommands; + @Override public boolean getLimitCommands() { return limitCommands; } @@ -224,10 +219,9 @@ public class User extends ExtendedModel { this.limitCommands = limitCommands; } - private String poiLayer; - private boolean disableReports; + @Override public boolean getDisableReports() { return disableReports; } @@ -236,6 +230,19 @@ public class User extends ExtendedModel { this.disableReports = disableReports; } + private boolean fixedEmail; + + @Override + public boolean getFixedEmail() { + return fixedEmail; + } + + public void setFixedEmail(boolean fixedEmail) { + this.fixedEmail = fixedEmail; + } + + private String poiLayer; + public String getPoiLayer() { return poiLayer; } @@ -261,12 +268,12 @@ public class User extends ExtendedModel { private String hashedPassword; @JsonIgnore - @QueryExtended + @QueryIgnore public String getHashedPassword() { return hashedPassword; } - @QueryExtended + @QueryIgnore public void setHashedPassword(String hashedPassword) { this.hashedPassword = hashedPassword; } @@ -274,12 +281,12 @@ public class User extends ExtendedModel { private String salt; @JsonIgnore - @QueryExtended + @QueryIgnore public String getSalt() { return salt; } - @QueryExtended + @QueryIgnore public void setSalt(String salt) { this.salt = salt; } diff --git a/src/main/java/org/traccar/model/UserRestrictions.java b/src/main/java/org/traccar/model/UserRestrictions.java new file mode 100644 index 000000000..1fcc5682e --- /dev/null +++ b/src/main/java/org/traccar/model/UserRestrictions.java @@ -0,0 +1,24 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.model; + +public interface UserRestrictions { + boolean getReadonly(); + boolean getDeviceReadonly(); + boolean getLimitCommands(); + boolean getDisableReports(); + boolean getFixedEmail(); +} diff --git a/src/main/java/org/traccar/model/WifiAccessPoint.java b/src/main/java/org/traccar/model/WifiAccessPoint.java index 87a77f3c0..e28c1b935 100644 --- a/src/main/java/org/traccar/model/WifiAccessPoint.java +++ b/src/main/java/org/traccar/model/WifiAccessPoint.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,8 @@ package org.traccar.model; import com.fasterxml.jackson.annotation.JsonInclude; +import java.util.Objects; + @JsonInclude(JsonInclude.Include.NON_NULL) public class WifiAccessPoint { @@ -63,4 +65,23 @@ public class WifiAccessPoint { this.channel = channel; } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + WifiAccessPoint that = (WifiAccessPoint) o; + return Objects.equals(macAddress, that.macAddress) + && Objects.equals(signalStrength, that.signalStrength) + && Objects.equals(channel, that.channel); + } + + @Override + public int hashCode() { + return Objects.hash(macAddress, signalStrength, channel); + } + } diff --git a/src/main/java/org/traccar/notification/EventForwarder.java b/src/main/java/org/traccar/notification/EventForwarder.java deleted file mode 100644 index c908fedbd..000000000 --- a/src/main/java/org/traccar/notification/EventForwarder.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2016 - 2020 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.notification; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traccar.Context; -import org.traccar.config.Keys; -import org.traccar.model.Device; -import org.traccar.model.Event; -import org.traccar.model.Geofence; -import org.traccar.model.Maintenance; -import org.traccar.model.Position; - -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.Invocation; -import javax.ws.rs.client.InvocationCallback; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -public class EventForwarder { - - private static final Logger LOGGER = LoggerFactory.getLogger(EventForwarder.class); - - private final String url; - private final String header; - - public EventForwarder() { - url = Context.getConfig().getString(Keys.EVENT_FORWARD_URL); - header = Context.getConfig().getString(Keys.EVENT_FORWARD_HEADERS); - } - - private static final String KEY_POSITION = "position"; - private static final String KEY_EVENT = "event"; - private static final String KEY_GEOFENCE = "geofence"; - private static final String KEY_DEVICE = "device"; - private static final String KEY_MAINTENANCE = "maintenance"; - private static final String KEY_USERS = "users"; - - public final void forwardEvent(Event event, Position position, Set<Long> users) { - - Invocation.Builder requestBuilder = Context.getClient().target(url).request(); - - if (header != null && !header.isEmpty()) { - for (String line: header.split("\\r?\\n")) { - String[] values = line.split(":", 2); - requestBuilder.header(values[0].trim(), values[1].trim()); - } - } - - LOGGER.debug("Event forwarding initiated"); - requestBuilder.async().post( - Entity.json(preparePayload(event, position, users)), new InvocationCallback<Object>() { - @Override - public void completed(Object o) { - LOGGER.debug("Event forwarding succeeded"); - } - - @Override - public void failed(Throwable throwable) { - LOGGER.warn("Event forwarding failed", throwable); - } - }); - } - - protected Map<String, Object> preparePayload(Event event, Position position, Set<Long> users) { - Map<String, Object> data = new HashMap<>(); - data.put(KEY_EVENT, event); - if (position != null) { - data.put(KEY_POSITION, position); - } - Device device = Context.getIdentityManager().getById(event.getDeviceId()); - if (device != null) { - data.put(KEY_DEVICE, device); - } - if (event.getGeofenceId() != 0) { - Geofence geofence = Context.getGeofenceManager().getById(event.getGeofenceId()); - if (geofence != null) { - data.put(KEY_GEOFENCE, geofence); - } - } - if (event.getMaintenanceId() != 0) { - Maintenance maintenance = Context.getMaintenancesManager().getById(event.getMaintenanceId()); - if (maintenance != null) { - data.put(KEY_MAINTENANCE, maintenance); - } - } - data.put(KEY_USERS, Context.getUsersManager().getItems(users)); - return data; - } - -} diff --git a/src/main/java/org/traccar/notification/NotificationFormatter.java b/src/main/java/org/traccar/notification/NotificationFormatter.java index 9a6723a71..85e8a54bb 100644 --- a/src/main/java/org/traccar/notification/NotificationFormatter.java +++ b/src/main/java/org/traccar/notification/NotificationFormatter.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) * Copyright 2017 - 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,50 +17,59 @@ package org.traccar.notification; import org.apache.velocity.VelocityContext; -import org.traccar.Context; +import org.traccar.helper.model.UserUtil; import org.traccar.model.Device; import org.traccar.model.Event; +import org.traccar.model.Geofence; +import org.traccar.model.Maintenance; import org.traccar.model.Position; +import org.traccar.model.Server; import org.traccar.model.User; -import org.traccar.reports.ReportUtils; +import org.traccar.session.cache.CacheManager; -public final class NotificationFormatter { +import jakarta.inject.Inject; +import jakarta.inject.Singleton; - private NotificationFormatter() { +@Singleton +public class NotificationFormatter { + + private final CacheManager cacheManager; + private final TextTemplateFormatter textTemplateFormatter; + + @Inject + public NotificationFormatter( + CacheManager cacheManager, TextTemplateFormatter textTemplateFormatter) { + this.cacheManager = cacheManager; + this.textTemplateFormatter = textTemplateFormatter; } - public static VelocityContext prepareContext(long userId, Event event, Position position) { + public NotificationMessage formatMessage(User user, Event event, Position position, String templatePath) { - User user = Context.getPermissionsManager().getUser(userId); - Device device = Context.getIdentityManager().getById(event.getDeviceId()); + Server server = cacheManager.getServer(); + Device device = cacheManager.getObject(Device.class, event.getDeviceId()); - VelocityContext velocityContext = TextTemplateFormatter.prepareContext(user); + VelocityContext velocityContext = textTemplateFormatter.prepareContext(server, user); velocityContext.put("device", device); velocityContext.put("event", event); if (position != null) { velocityContext.put("position", position); - velocityContext.put("speedUnit", ReportUtils.getSpeedUnit(userId)); - velocityContext.put("distanceUnit", ReportUtils.getDistanceUnit(userId)); - velocityContext.put("volumeUnit", ReportUtils.getVolumeUnit(userId)); + velocityContext.put("speedUnit", UserUtil.getSpeedUnit(server, user)); + velocityContext.put("distanceUnit", UserUtil.getDistanceUnit(server, user)); + velocityContext.put("volumeUnit", UserUtil.getVolumeUnit(server, user)); } if (event.getGeofenceId() != 0) { - velocityContext.put("geofence", Context.getGeofenceManager().getById(event.getGeofenceId())); + velocityContext.put("geofence", cacheManager.getObject(Geofence.class, event.getGeofenceId())); } if (event.getMaintenanceId() != 0) { - velocityContext.put("maintenance", Context.getMaintenancesManager().getById(event.getMaintenanceId())); + velocityContext.put("maintenance", cacheManager.getObject(Maintenance.class, event.getMaintenanceId())); } String driverUniqueId = event.getString(Position.KEY_DRIVER_UNIQUE_ID); if (driverUniqueId != null) { - velocityContext.put("driver", Context.getDriversManager().getDriverByUniqueId(driverUniqueId)); + velocityContext.put("driver", cacheManager.findDriverByUniqueId(device.getId(), driverUniqueId)); } - return velocityContext; - } - - public static NotificationMessage formatMessage(long userId, Event event, Position position, String templatePath) { - VelocityContext velocityContext = prepareContext(userId, event, position); - return TextTemplateFormatter.formatMessage(velocityContext, event.getType(), templatePath); + return textTemplateFormatter.formatMessage(velocityContext, event.getType(), templatePath); } } diff --git a/src/main/java/org/traccar/notification/NotificatorManager.java b/src/main/java/org/traccar/notification/NotificatorManager.java index dfd8cd3d6..8d41ee354 100644 --- a/src/main/java/org/traccar/notification/NotificatorManager.java +++ b/src/main/java/org/traccar/notification/NotificatorManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2018 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,87 +16,67 @@ */ package org.traccar.notification; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traccar.Context; +import com.google.inject.Injector; +import org.traccar.config.Config; +import org.traccar.config.Keys; import org.traccar.model.Typed; +import org.traccar.notificators.Notificator; +import org.traccar.notificators.NotificatorCommand; import org.traccar.notificators.NotificatorFirebase; import org.traccar.notificators.NotificatorMail; -import org.traccar.notificators.NotificatorNull; -import org.traccar.notificators.Notificator; +import org.traccar.notificators.NotificatorPushover; import org.traccar.notificators.NotificatorSms; +import org.traccar.notificators.NotificatorTelegram; import org.traccar.notificators.NotificatorTraccar; import org.traccar.notificators.NotificatorWeb; -import org.traccar.notificators.NotificatorTelegram; -import org.traccar.notificators.NotificatorPushover; -public final class NotificatorManager { +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; - private static final Logger LOGGER = LoggerFactory.getLogger(NotificatorManager.class); +@Singleton +public class NotificatorManager { - private static final Notificator NULL_NOTIFICATOR = new NotificatorNull(); + private static final Map<String, Class<? extends Notificator>> NOTIFICATORS_ALL = Map.of( + "command", NotificatorCommand.class, + "web", NotificatorWeb.class, + "mail", NotificatorMail.class, + "sms", NotificatorSms.class, + "firebase", NotificatorFirebase.class, + "traccar", NotificatorTraccar.class, + "telegram", NotificatorTelegram.class, + "pushover", NotificatorPushover.class); - private final Map<String, Notificator> notificators = new HashMap<>(); + private final Injector injector; - public NotificatorManager() { - final String[] types = Context.getConfig().getString("notificator.types", "").split(","); - for (String type : types) { - String defaultNotificator = ""; - switch (type) { - case "web": - defaultNotificator = NotificatorWeb.class.getCanonicalName(); - break; - case "mail": - defaultNotificator = NotificatorMail.class.getCanonicalName(); - break; - case "sms": - defaultNotificator = NotificatorSms.class.getCanonicalName(); - break; - case "firebase": - defaultNotificator = NotificatorFirebase.class.getCanonicalName(); - break; - case "traccar": - defaultNotificator = NotificatorTraccar.class.getCanonicalName(); - break; - case "telegram": - defaultNotificator = NotificatorTelegram.class.getCanonicalName(); - break; - case "pushover": - defaultNotificator = NotificatorPushover.class.getCanonicalName(); - break; - default: - break; - } - final String className = Context.getConfig() - .getString("notificator." + type + ".class", defaultNotificator); - try { - notificators.put(type, (Notificator) Class.forName(className).newInstance()); - } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { - LOGGER.warn("Unable to load notificator class for " + type + " " + className + " " + e.getMessage()); - } + private final Set<String> types = new HashSet<>(); + + @Inject + public NotificatorManager(Injector injector, Config config) { + this.injector = injector; + String types = config.getString(Keys.NOTIFICATOR_TYPES); + if (types != null) { + this.types.addAll(Arrays.asList(types.split(","))); } } public Notificator getNotificator(String type) { - final Notificator notificator = notificators.get(type); - if (notificator == null) { - LOGGER.warn("No notificator configured for type : " + type); - return NULL_NOTIFICATOR; + var clazz = NOTIFICATORS_ALL.get(type); + if (clazz != null && types.contains(type)) { + var notificator = injector.getInstance(clazz); + if (notificator != null) { + return notificator; + } } - return notificator; + throw new RuntimeException("Failed to get notificator " + type); } public Set<Typed> getAllNotificatorTypes() { - Set<Typed> result = new HashSet<>(); - for (String notificator : notificators.keySet()) { - result.add(new Typed(notificator)); - } - return result; + return types.stream().map(Typed::new).collect(Collectors.toUnmodifiableSet()); } } diff --git a/src/main/java/org/traccar/notification/PropertiesProvider.java b/src/main/java/org/traccar/notification/PropertiesProvider.java index f0078feef..91887b5d4 100644 --- a/src/main/java/org/traccar/notification/PropertiesProvider.java +++ b/src/main/java/org/traccar/notification/PropertiesProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package org.traccar.notification; import org.traccar.config.Config; +import org.traccar.config.ConfigKey; import org.traccar.model.ExtendedModel; public class PropertiesProvider { @@ -32,36 +33,29 @@ public class PropertiesProvider { this.extendedModel = extendedModel; } - public String getString(String key) { + public String getString(ConfigKey<String> key) { if (config != null) { return config.getString(key); } else { - return extendedModel.getString(key); + String result = extendedModel.getString(key.getKey()); + return result != null ? result : key.getDefaultValue(); } } - public String getString(String key, String defaultValue) { - String value = getString(key); - if (value == null) { - value = defaultValue; - } - return value; - } - - public int getInteger(String key, int defaultValue) { + public int getInteger(ConfigKey<Integer> key) { if (config != null) { - return config.getInteger(key, defaultValue); + return config.getInteger(key); } else { - Object result = extendedModel.getAttributes().get(key); + Object result = extendedModel.getAttributes().get(key.getKey()); if (result != null) { return result instanceof String ? Integer.parseInt((String) result) : (Integer) result; } else { - return defaultValue; + return key.getDefaultValue(); } } } - public Boolean getBoolean(String key) { + public Boolean getBoolean(ConfigKey<Boolean> key) { if (config != null) { if (config.hasKey(key)) { return config.getBoolean(key); @@ -69,7 +63,7 @@ public class PropertiesProvider { return null; } } else { - Object result = extendedModel.getAttributes().get(key); + Object result = extendedModel.getAttributes().get(key.getKey()); if (result != null) { return result instanceof String ? Boolean.valueOf((String) result) : (Boolean) result; } else { diff --git a/src/main/java/org/traccar/notification/TextTemplateFormatter.java b/src/main/java/org/traccar/notification/TextTemplateFormatter.java index b7058c824..ab90d76d4 100644 --- a/src/main/java/org/traccar/notification/TextTemplateFormatter.java +++ b/src/main/java/org/traccar/notification/TextTemplateFormatter.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,37 +17,56 @@ package org.traccar.notification; import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.VelocityEngine; import org.apache.velocity.exception.ResourceNotFoundException; import org.apache.velocity.tools.generic.DateTool; import org.apache.velocity.tools.generic.NumberTool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.traccar.Context; +import org.traccar.api.signature.TokenManager; +import org.traccar.helper.model.UserUtil; +import org.traccar.model.Server; import org.traccar.model.User; -import org.traccar.reports.ReportUtils; +import org.traccar.storage.StorageException; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import java.io.IOException; import java.io.StringWriter; import java.nio.charset.StandardCharsets; import java.nio.file.Paths; +import java.security.GeneralSecurityException; import java.util.Locale; -public final class TextTemplateFormatter { +@Singleton +public class TextTemplateFormatter { private static final Logger LOGGER = LoggerFactory.getLogger(TextTemplateFormatter.class); - private TextTemplateFormatter() { + private final VelocityEngine velocityEngine; + private final TokenManager tokenManager; + + @Inject + public TextTemplateFormatter(VelocityEngine velocityEngine, TokenManager tokenManager) { + this.velocityEngine = velocityEngine; + this.tokenManager = tokenManager; } - public static VelocityContext prepareContext(User user) { + public VelocityContext prepareContext(Server server, User user) { VelocityContext velocityContext = new VelocityContext(); if (user != null) { velocityContext.put("user", user); - velocityContext.put("timezone", ReportUtils.getTimezone(user.getId())); + velocityContext.put("timezone", UserUtil.getTimezone(server, user)); + try { + velocityContext.put("token", tokenManager.generateToken(user.getId())); + } catch (IOException | GeneralSecurityException | StorageException e) { + LOGGER.warn("Token generation failed", e); + } } - velocityContext.put("webUrl", Context.getVelocityEngine().getProperty("web.url")); + velocityContext.put("webUrl", velocityEngine.getProperty("web.url")); velocityContext.put("dateTool", new DateTool()); velocityContext.put("numberTool", new NumberTool()); velocityContext.put("locale", Locale.getDefault()); @@ -55,23 +74,23 @@ public final class TextTemplateFormatter { return velocityContext; } - public static Template getTemplate(String name, String path) { + public Template getTemplate(String name, String path) { String templateFilePath; Template template; try { templateFilePath = Paths.get(path, name + ".vm").toString(); - template = Context.getVelocityEngine().getTemplate(templateFilePath, StandardCharsets.UTF_8.name()); + template = velocityEngine.getTemplate(templateFilePath, StandardCharsets.UTF_8.name()); } catch (ResourceNotFoundException error) { LOGGER.warn("Notification template error", error); templateFilePath = Paths.get(path, "unknown.vm").toString(); - template = Context.getVelocityEngine().getTemplate(templateFilePath, StandardCharsets.UTF_8.name()); + template = velocityEngine.getTemplate(templateFilePath, StandardCharsets.UTF_8.name()); } return template; } - public static NotificationMessage formatMessage(VelocityContext velocityContext, String name, String templatePath) { + public NotificationMessage formatMessage(VelocityContext velocityContext, String name, String templatePath) { StringWriter writer = new StringWriter(); getTemplate(name, templatePath).merge(velocityContext, writer); return new NotificationMessage((String) velocityContext.get("subject"), writer.toString()); diff --git a/src/main/java/org/traccar/notificators/Notificator.java b/src/main/java/org/traccar/notificators/Notificator.java index dd888bae9..cf71141c0 100644 --- a/src/main/java/org/traccar/notificators/Notificator.java +++ b/src/main/java/org/traccar/notificators/Notificator.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2018 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,27 +16,14 @@ */ package org.traccar.notificators; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.traccar.model.Event; +import org.traccar.model.Notification; import org.traccar.model.Position; +import org.traccar.model.User; import org.traccar.notification.MessageException; -public abstract class Notificator { +public interface Notificator { - private static final Logger LOGGER = LoggerFactory.getLogger(Notificator.class); - - public void sendAsync(final long userId, final Event event, final Position position) { - new Thread(() -> { - try { - sendSync(userId, event, position); - } catch (MessageException | InterruptedException error) { - LOGGER.warn("Event send error", error); - } - }).start(); - } - - public abstract void sendSync(long userId, Event event, Position position) - throws MessageException, InterruptedException; + void send(Notification notification, User user, Event event, Position position) throws MessageException; } diff --git a/src/main/java/org/traccar/notificators/NotificatorCommand.java b/src/main/java/org/traccar/notificators/NotificatorCommand.java new file mode 100644 index 000000000..5dd3313d4 --- /dev/null +++ b/src/main/java/org/traccar/notificators/NotificatorCommand.java @@ -0,0 +1,62 @@ +/* + * 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.notificators; + +import org.traccar.database.CommandsManager; +import org.traccar.model.Command; +import org.traccar.model.Event; +import org.traccar.model.Notification; +import org.traccar.model.Position; +import org.traccar.model.User; +import org.traccar.notification.MessageException; +import org.traccar.storage.Storage; +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; + +@Singleton +public class NotificatorCommand implements Notificator { + + private final Storage storage; + private final CommandsManager commandsManager; + + @Inject + public NotificatorCommand(Storage storage, CommandsManager commandsManager) { + this.storage = storage; + this.commandsManager = commandsManager; + } + + @Override + public void send(Notification notification, User user, Event event, Position position) throws MessageException { + + if (notification == null || notification.getCommandId() <= 0) { + throw new MessageException("Saved command not provided"); + } + + try { + Command command = storage.getObject(Command.class, new Request( + new Columns.All(), new Condition.Equals("id", notification.getCommandId()))); + command.setDeviceId(event.getDeviceId()); + commandsManager.sendCommand(command); + } catch (Exception e) { + throw new MessageException(e); + } + } + +} diff --git a/src/main/java/org/traccar/notificators/NotificatorFirebase.java b/src/main/java/org/traccar/notificators/NotificatorFirebase.java index f91ec25a0..be95fb28e 100644 --- a/src/main/java/org/traccar/notificators/NotificatorFirebase.java +++ b/src/main/java/org/traccar/notificators/NotificatorFirebase.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2018 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,88 +16,131 @@ */ package org.traccar.notificators; -import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.auth.oauth2.GoogleCredentials; +import com.google.firebase.FirebaseApp; +import com.google.firebase.FirebaseOptions; +import com.google.firebase.messaging.AndroidConfig; +import com.google.firebase.messaging.AndroidNotification; +import com.google.firebase.messaging.ApnsConfig; +import com.google.firebase.messaging.Aps; +import com.google.firebase.messaging.FirebaseMessaging; +import com.google.firebase.messaging.FirebaseMessagingException; +import com.google.firebase.messaging.MessagingErrorCode; +import com.google.firebase.messaging.MulticastMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.traccar.Context; +import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.model.Event; +import org.traccar.model.Notification; import org.traccar.model.Position; import org.traccar.model.User; -import org.traccar.notification.NotificationMessage; +import org.traccar.notification.MessageException; import org.traccar.notification.NotificationFormatter; - -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.InvocationCallback; - -public class NotificatorFirebase extends Notificator { +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.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +@Singleton +public class NotificatorFirebase implements Notificator { private static final Logger LOGGER = LoggerFactory.getLogger(NotificatorFirebase.class); - private final String url; - private final String key; + private final NotificationFormatter notificationFormatter; + private final Storage storage; + private final CacheManager cacheManager; - public static class Notification { - @JsonProperty("title") - private String title; - @JsonProperty("body") - private String body; - @JsonProperty("sound") - private String sound; - } + @Inject + public NotificatorFirebase( + Config config, NotificationFormatter notificationFormatter, + Storage storage, CacheManager cacheManager) throws IOException { - public static class Message { - @JsonProperty("registration_ids") - private String[] tokens; - @JsonProperty("notification") - private Notification notification; - } + this.notificationFormatter = notificationFormatter; + this.storage = storage; + this.cacheManager = cacheManager; - public NotificatorFirebase() { - this( - "https://fcm.googleapis.com/fcm/send", - Context.getConfig().getString(Keys.NOTIFICATOR_FIREBASE_KEY)); - } + InputStream serviceAccount = new ByteArrayInputStream( + config.getString(Keys.NOTIFICATOR_FIREBASE_SERVICE_ACCOUNT).getBytes()); - protected NotificatorFirebase(String url, String key) { - this.url = url; - this.key = key; + FirebaseOptions options = FirebaseOptions.builder() + .setCredentials(GoogleCredentials.fromStream(serviceAccount)) + .build(); + + FirebaseApp.initializeApp(options); } @Override - public void sendSync(long userId, Event event, Position position) { - final User user = Context.getPermissionsManager().getUser(userId); - if (user.getAttributes().containsKey("notificationTokens")) { - - NotificationMessage shortMessage = NotificationFormatter.formatMessage(userId, event, position, "short"); - - Notification notification = new Notification(); - notification.title = shortMessage.getSubject(); - notification.body = shortMessage.getBody(); - notification.sound = "default"; - - Message message = new Message(); - message.tokens = user.getString("notificationTokens").split("[, ]"); - message.notification = notification; - - Context.getClient().target(url).request() - .header("Authorization", "key=" + key) - .async().post(Entity.json(message), new InvocationCallback<Object>() { - @Override - public void completed(Object o) { + public void send(Notification notification, User user, Event event, Position position) throws MessageException { + if (user.hasAttribute("notificationTokens")) { + + var shortMessage = notificationFormatter.formatMessage(user, event, position, "short"); + + List<String> registrationTokens = new ArrayList<>( + Arrays.asList(user.getString("notificationTokens").split("[, ]"))); + + MulticastMessage message = MulticastMessage.builder() + .setNotification(com.google.firebase.messaging.Notification.builder() + .setTitle(shortMessage.getSubject()) + .setBody(shortMessage.getBody()) + .build()) + .setAndroidConfig(AndroidConfig.builder() + .setNotification(AndroidNotification.builder() + .setSound("default") + .build()) + .build()) + .setApnsConfig(ApnsConfig.builder() + .setAps(Aps.builder() + .setSound("default") + .build()) + .build()) + .addAllTokens(registrationTokens) + .putData("eventId", String.valueOf(event.getId())) + .build(); + + try { + var result = FirebaseMessaging.getInstance().sendMulticast(message); + List<String> failedTokens = new LinkedList<>(); + var iterator = result.getResponses().listIterator(); + while (iterator.hasNext()) { + int index = iterator.nextIndex(); + var response = iterator.next(); + if (!response.isSuccessful()) { + MessagingErrorCode error = response.getException().getMessagingErrorCode(); + if (error == MessagingErrorCode.INVALID_ARGUMENT || error == MessagingErrorCode.UNREGISTERED) { + failedTokens.add(registrationTokens.get(index)); + } + LOGGER.warn("Firebase user {} error", user.getId(), response.getException()); + } } - - @Override - public void failed(Throwable throwable) { - LOGGER.warn("Firebase notification error", throwable); + if (!failedTokens.isEmpty()) { + registrationTokens.removeAll(failedTokens); + if (registrationTokens.isEmpty()) { + user.getAttributes().remove("notificationTokens"); + } else { + user.set("notificationTokens", String.join(",", registrationTokens)); + } + storage.updateObject(user, new Request( + new Columns.Include("attributes"), + new Condition.Equals("id", user.getId()))); + cacheManager.updateOrInvalidate(true, user); } - }); + } catch (FirebaseMessagingException | StorageException e) { + LOGGER.warn("Firebase error", e); + } } } - @Override - public void sendAsync(long userId, Event event, Position position) { - sendSync(userId, event, position); - } - } diff --git a/src/main/java/org/traccar/notificators/NotificatorMail.java b/src/main/java/org/traccar/notificators/NotificatorMail.java index 9b5637ed8..3ab050686 100644 --- a/src/main/java/org/traccar/notificators/NotificatorMail.java +++ b/src/main/java/org/traccar/notificators/NotificatorMail.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2017 - 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,22 +16,35 @@ */ package org.traccar.notificators; -import org.traccar.Context; +import org.traccar.mail.MailManager; import org.traccar.model.Event; +import org.traccar.model.Notification; import org.traccar.model.Position; -import org.traccar.notification.NotificationMessage; +import org.traccar.model.User; import org.traccar.notification.MessageException; import org.traccar.notification.NotificationFormatter; -import javax.mail.MessagingException; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import jakarta.mail.MessagingException; -public final class NotificatorMail extends Notificator { +@Singleton +public class NotificatorMail implements Notificator { + + private final MailManager mailManager; + private final NotificationFormatter notificationFormatter; + + @Inject + public NotificatorMail(MailManager mailManager, NotificationFormatter notificationFormatter) { + this.mailManager = mailManager; + this.notificationFormatter = notificationFormatter; + } @Override - public void sendSync(long userId, Event event, Position position) throws MessageException { + public void send(Notification notification, User user, Event event, Position position) throws MessageException { try { - NotificationMessage fullMessage = NotificationFormatter.formatMessage(userId, event, position, "full"); - Context.getMailManager().sendMessage(userId, fullMessage.getSubject(), fullMessage.getBody()); + var fullMessage = notificationFormatter.formatMessage(user, event, position, "full"); + mailManager.sendMessage(user, false, fullMessage.getSubject(), fullMessage.getBody()); } catch (MessagingException e) { throw new MessageException(e); } diff --git a/src/main/java/org/traccar/notificators/NotificatorNull.java b/src/main/java/org/traccar/notificators/NotificatorNull.java deleted file mode 100644 index 9364336be..000000000 --- a/src/main/java/org/traccar/notificators/NotificatorNull.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2018 Anton Tananaev (anton@traccar.org) - * Copyright 2018 Andrey Kunitsyn (andrey@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.notificators; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traccar.model.Event; -import org.traccar.model.Position; - -public final class NotificatorNull extends Notificator { - - private static final Logger LOGGER = LoggerFactory.getLogger(NotificatorNull.class); - - @Override - public void sendAsync(long userId, Event event, Position position) { - LOGGER.warn("You are using null notificatior, please check your configuration, notification not sent"); - } - - @Override - public void sendSync(long userId, Event event, Position position) { - LOGGER.warn("You are using null notificatior, please check your configuration, notification not sent"); - } - -} diff --git a/src/main/java/org/traccar/notificators/NotificatorPushover.java b/src/main/java/org/traccar/notificators/NotificatorPushover.java index 456c2fe4f..9f2a8c94d 100644 --- a/src/main/java/org/traccar/notificators/NotificatorPushover.java +++ b/src/main/java/org/traccar/notificators/NotificatorPushover.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2020 - 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. @@ -16,22 +16,24 @@ package org.traccar.notificators; import com.fasterxml.jackson.annotation.JsonProperty; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traccar.Context; +import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.model.Event; +import org.traccar.model.Notification; import org.traccar.model.Position; import org.traccar.model.User; import org.traccar.notification.NotificationFormatter; -import org.traccar.notification.NotificationMessage; -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.InvocationCallback; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.Entity; -public class NotificatorPushover extends Notificator { +@Singleton +public class NotificatorPushover implements Notificator { - private static final Logger LOGGER = LoggerFactory.getLogger(NotificatorPushover.class); + private final NotificationFormatter notificationFormatter; + private final Client client; private final String url; private final String token; @@ -50,58 +52,35 @@ public class NotificatorPushover extends Notificator { private String message; } - public NotificatorPushover() { + @Inject + public NotificatorPushover(Config config, NotificationFormatter notificationFormatter, Client client) { + this.notificationFormatter = notificationFormatter; + this.client = client; url = "https://api.pushover.net/1/messages.json"; - token = Context.getConfig().getString(Keys.NOTIFICATOR_PUSHOVER_TOKEN); - user = Context.getConfig().getString(Keys.NOTIFICATOR_PUSHOVER_USER); + token = config.getString(Keys.NOTIFICATOR_PUSHOVER_TOKEN); + user = config.getString(Keys.NOTIFICATOR_PUSHOVER_USER); } @Override - public void sendSync(long userId, Event event, Position position) { + public void send(Notification notification, User user, Event event, Position position) { + var shortMessage = notificationFormatter.formatMessage(user, event, position, "short"); - final User user = Context.getPermissionsManager().getUser(userId); - - String device = ""; - - if (user.getAttributes().containsKey("notificator.pushover.device")) { - device = user.getString("notificator.pushover.device").replaceAll(" *, *", ","); - } + Message message = new Message(); + message.token = token; - if (token == null) { - LOGGER.warn("Pushover token not found"); - return; + message.user = user.getString("pushoverUserKey"); + if (message.user == null) { + message.user = this.user; } - if (this.user == null) { - LOGGER.warn("Pushover user not found"); - return; + if (user.hasAttribute("pushoverDeviceNames")) { + message.device = user.getString("pushoverDeviceNames").replaceAll(" *, *", ","); } - NotificationMessage shortMessage = NotificationFormatter.formatMessage(userId, event, position, "short"); - - Message message = new Message(); - message.token = token; - message.user = this.user; - message.device = device; message.title = shortMessage.getSubject(); message.message = shortMessage.getBody(); - Context.getClient().target(url).request() - .async().post(Entity.json(message), new InvocationCallback<Object>() { - @Override - public void completed(Object o) { - } - - @Override - public void failed(Throwable throwable) { - LOGGER.warn("Pushover API error", throwable); - } - }); - } - - @Override - public void sendAsync(long userId, Event event, Position position) { - sendSync(userId, event, position); + client.target(url).request().post(Entity.json(message)).close(); } } diff --git a/src/main/java/org/traccar/notificators/NotificatorSms.java b/src/main/java/org/traccar/notificators/NotificatorSms.java index fb817b112..2b6b20b1b 100644 --- a/src/main/java/org/traccar/notificators/NotificatorSms.java +++ b/src/main/java/org/traccar/notificators/NotificatorSms.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2017 - 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,37 +16,39 @@ */ package org.traccar.notificators; -import org.traccar.Context; -import org.traccar.Main; import org.traccar.database.StatisticsManager; import org.traccar.model.Event; +import org.traccar.model.Notification; import org.traccar.model.Position; import org.traccar.model.User; import org.traccar.notification.MessageException; import org.traccar.notification.NotificationFormatter; -import org.traccar.notification.NotificationMessage; +import org.traccar.sms.SmsManager; -public final class NotificatorSms extends Notificator { +import jakarta.inject.Inject; +import jakarta.inject.Singleton; - @Override - public void sendAsync(long userId, Event event, Position position) { - final User user = Context.getPermissionsManager().getUser(userId); - if (user.getPhone() != null) { - NotificationMessage shortMessage = NotificationFormatter.formatMessage(userId, event, position, "short"); - Main.getInjector().getInstance(StatisticsManager.class).registerSms(); - Context.getSmsManager().sendMessageAsync(user.getPhone(), - shortMessage.getBody(), false); - } +@Singleton +public class NotificatorSms implements Notificator { + + private final SmsManager smsManager; + private final NotificationFormatter notificationFormatter; + private final StatisticsManager statisticsManager; + + @Inject + public NotificatorSms( + SmsManager smsManager, NotificationFormatter notificationFormatter, StatisticsManager statisticsManager) { + this.smsManager = smsManager; + this.notificationFormatter = notificationFormatter; + this.statisticsManager = statisticsManager; } @Override - public void sendSync(long userId, Event event, Position position) throws MessageException, InterruptedException { - final User user = Context.getPermissionsManager().getUser(userId); + public void send(Notification notification, User user, Event event, Position position) throws MessageException { if (user.getPhone() != null) { - NotificationMessage shortMessage = NotificationFormatter.formatMessage(userId, event, position, "short"); - Main.getInjector().getInstance(StatisticsManager.class).registerSms(); - Context.getSmsManager().sendMessageSync(user.getPhone(), - shortMessage.getBody(), false); + var shortMessage = notificationFormatter.formatMessage(user, event, position, "short"); + statisticsManager.registerSms(); + smsManager.sendMessage(user.getPhone(), shortMessage.getBody(), false); } } diff --git a/src/main/java/org/traccar/notificators/NotificatorTelegram.java b/src/main/java/org/traccar/notificators/NotificatorTelegram.java index 70148110c..c91aaa4ff 100644 --- a/src/main/java/org/traccar/notificators/NotificatorTelegram.java +++ b/src/main/java/org/traccar/notificators/NotificatorTelegram.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2019 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2021 Rafael Miquelino (rafaelmiquelino@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,22 +17,24 @@ package org.traccar.notificators; import com.fasterxml.jackson.annotation.JsonProperty; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traccar.Context; -import org.traccar.model.User; +import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.model.Event; +import org.traccar.model.Notification; import org.traccar.model.Position; +import org.traccar.model.User; import org.traccar.notification.NotificationFormatter; -import org.traccar.notification.NotificationMessage; -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.InvocationCallback; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.Entity; -public class NotificatorTelegram extends Notificator { +@Singleton +public class NotificatorTelegram implements Notificator { - private static final Logger LOGGER = LoggerFactory.getLogger(NotificatorTelegram.class); + private final NotificationFormatter notificationFormatter; + private final Client client; private final String urlSendText; private final String urlSendLocation; @@ -61,29 +63,16 @@ public class NotificatorTelegram extends Notificator { private int bearing; } - public NotificatorTelegram() { + @Inject + public NotificatorTelegram(Config config, NotificationFormatter notificationFormatter, Client client) { + this.notificationFormatter = notificationFormatter; + this.client = client; urlSendText = String.format( - "https://api.telegram.org/bot%s/sendMessage", - Context.getConfig().getString(Keys.NOTIFICATOR_TELEGRAM_KEY)); + "https://api.telegram.org/bot%s/sendMessage", config.getString(Keys.NOTIFICATOR_TELEGRAM_KEY)); urlSendLocation = String.format( - "https://api.telegram.org/bot%s/sendLocation", - Context.getConfig().getString(Keys.NOTIFICATOR_TELEGRAM_KEY)); - chatId = Context.getConfig().getString(Keys.NOTIFICATOR_TELEGRAM_CHAT_ID); - sendLocation = Context.getConfig().getBoolean(Keys.NOTIFICATOR_TELEGRAM_SEND_LOCATION); - } - - private void executeRequest(String url, Object message) { - Context.getClient().target(url).request() - .async().post(Entity.json(message), new InvocationCallback<Object>() { - @Override - public void completed(Object o) { - } - - @Override - public void failed(Throwable throwable) { - LOGGER.warn("Telegram API error", throwable); - } - }); + "https://api.telegram.org/bot%s/sendLocation", config.getString(Keys.NOTIFICATOR_TELEGRAM_KEY)); + chatId = config.getString(Keys.NOTIFICATOR_TELEGRAM_CHAT_ID); + sendLocation = config.getBoolean(Keys.NOTIFICATOR_TELEGRAM_SEND_LOCATION); } private LocationMessage createLocationMessage(String messageChatId, Position position) { @@ -97,9 +86,8 @@ public class NotificatorTelegram extends Notificator { } @Override - public void sendSync(long userId, Event event, Position position) { - User user = Context.getPermissionsManager().getUser(userId); - NotificationMessage shortMessage = NotificationFormatter.formatMessage(userId, event, position, "short"); + public void send(Notification notification, User user, Event event, Position position) { + var shortMessage = notificationFormatter.formatMessage(user, event, position, "short"); TextMessage message = new TextMessage(); message.chatId = user.getString("telegramChatId"); @@ -107,15 +95,11 @@ public class NotificatorTelegram extends Notificator { message.chatId = chatId; } message.text = shortMessage.getBody(); - executeRequest(urlSendText, message); + client.target(urlSendText).request().post(Entity.json(message)).close(); if (sendLocation && position != null) { - executeRequest(urlSendLocation, createLocationMessage(message.chatId, position)); + client.target(urlSendLocation).request().post( + Entity.json(createLocationMessage(message.chatId, position))).close(); } } - @Override - public void sendAsync(long userId, Event event, Position position) { - sendSync(userId, event, position); - } - } diff --git a/src/main/java/org/traccar/notificators/NotificatorTraccar.java b/src/main/java/org/traccar/notificators/NotificatorTraccar.java index 5bcd18b5e..e354adccb 100644 --- a/src/main/java/org/traccar/notificators/NotificatorTraccar.java +++ b/src/main/java/org/traccar/notificators/NotificatorTraccar.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2020 - 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. @@ -15,15 +15,125 @@ */ package org.traccar.notificators; -import org.traccar.Context; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.config.Config; import org.traccar.config.Keys; +import org.traccar.model.Event; +import org.traccar.model.Position; +import org.traccar.model.User; +import org.traccar.notification.NotificationFormatter; +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 NotificatorTraccar extends NotificatorFirebase { +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.core.Response; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; - public NotificatorTraccar() { - super( - "https://www.traccar.org/push/", - Context.getConfig().getString(Keys.NOTIFICATOR_TRACCAR_KEY)); +@Singleton +public class NotificatorTraccar implements Notificator { + + private static final Logger LOGGER = LoggerFactory.getLogger(NotificatorTraccar.class); + + private final NotificationFormatter notificationFormatter; + private final Client client; + private final Storage storage; + private final CacheManager cacheManager; + + private final String url; + private final String key; + + public static class NotificationObject { + @JsonProperty("title") + private String title; + @JsonProperty("body") + private String body; + @JsonProperty("sound") + private String sound; + } + + public static class Message { + @JsonProperty("registration_ids") + private String[] tokens; + @JsonProperty("notification") + private NotificationObject notification; + } + + @Inject + public NotificatorTraccar( + Config config, NotificationFormatter notificationFormatter, Client client, + Storage storage, CacheManager cacheManager) { + this.notificationFormatter = notificationFormatter; + this.client = client; + this.storage = storage; + this.cacheManager = cacheManager; + this.url = "https://www.traccar.org/push/"; + this.key = config.getString(Keys.NOTIFICATOR_TRACCAR_KEY); + } + + @Override + public void send(org.traccar.model.Notification notification, User user, Event event, Position position) { + if (user.hasAttribute("notificationTokens")) { + + var shortMessage = notificationFormatter.formatMessage(user, event, position, "short"); + + NotificationObject item = new NotificationObject(); + item.title = shortMessage.getSubject(); + item.body = shortMessage.getBody(); + item.sound = "default"; + + String[] tokenArray = user.getString("notificationTokens").split("[, ]"); + List<String> registrationTokens = new ArrayList<>(Arrays.asList(tokenArray)); + + Message message = new Message(); + message.tokens = user.getString("notificationTokens").split("[, ]"); + message.notification = item; + + var request = client.target(url).request().header("Authorization", "key=" + key); + try (Response result = request.post(Entity.json(message))) { + var json = result.readEntity(JsonObject.class); + List<String> failedTokens = new LinkedList<>(); + var responses = json.getJsonArray("responses"); + for (int i = 0; i < responses.size(); i++) { + var response = responses.getJsonObject(i); + if (!response.getBoolean("success")) { + var error = response.getJsonObject("error"); + String errorCode = error.getString("code"); + if (errorCode.equals("messaging/invalid-argument") + || errorCode.equals("messaging/registration-token-not-registered")) { + failedTokens.add(registrationTokens.get(i)); + } + LOGGER.warn("Push user {} error - {}", user.getId(), error.getString("message")); + } + } + if (!failedTokens.isEmpty()) { + registrationTokens.removeAll(failedTokens); + if (registrationTokens.isEmpty()) { + user.getAttributes().remove("notificationTokens"); + } else { + user.set("notificationTokens", String.join(",", registrationTokens)); + } + storage.updateObject(user, new Request( + new Columns.Include("attributes"), + new Condition.Equals("id", user.getId()))); + cacheManager.updateOrInvalidate(true, user); + } + } catch (StorageException e) { + LOGGER.warn("Push error", e); + } + } } } diff --git a/src/main/java/org/traccar/notificators/NotificatorWeb.java b/src/main/java/org/traccar/notificators/NotificatorWeb.java index 1d11c0b46..3a125db3c 100644 --- a/src/main/java/org/traccar/notificators/NotificatorWeb.java +++ b/src/main/java/org/traccar/notificators/NotificatorWeb.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2018 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,15 +16,45 @@ */ package org.traccar.notificators; -import org.traccar.Context; import org.traccar.model.Event; +import org.traccar.model.Notification; import org.traccar.model.Position; +import org.traccar.model.User; +import org.traccar.notification.NotificationFormatter; +import org.traccar.session.ConnectionManager; -public final class NotificatorWeb extends Notificator { +import jakarta.inject.Inject; +import jakarta.inject.Singleton; + +@Singleton +public final class NotificatorWeb implements Notificator { + + private final ConnectionManager connectionManager; + private final NotificationFormatter notificationFormatter; + + @Inject + public NotificatorWeb(ConnectionManager connectionManager, NotificationFormatter notificationFormatter) { + this.connectionManager = connectionManager; + this.notificationFormatter = notificationFormatter; + } @Override - public void sendSync(long userId, Event event, Position position) { - Context.getConnectionManager().updateEvent(userId, event); + public void send(Notification notification, User user, Event event, Position position) { + + Event copy = new Event(); + copy.setId(event.getId()); + copy.setDeviceId(event.getDeviceId()); + copy.setType(event.getType()); + copy.setEventTime(event.getEventTime()); + copy.setPositionId(event.getPositionId()); + copy.setGeofenceId(event.getGeofenceId()); + copy.setMaintenanceId(event.getMaintenanceId()); + copy.getAttributes().putAll(event.getAttributes()); + + var message = notificationFormatter.formatMessage(user, event, position, "short"); + copy.set("message", message.getBody()); + + connectionManager.updateEvent(true, user.getId(), copy); } } diff --git a/src/main/java/org/traccar/protocol/AdmProtocol.java b/src/main/java/org/traccar/protocol/AdmProtocol.java index d1d81118c..3856dc906 100644 --- a/src/main/java/org/traccar/protocol/AdmProtocol.java +++ b/src/main/java/org/traccar/protocol/AdmProtocol.java @@ -19,17 +19,21 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class AdmProtocol extends BaseProtocol { - public AdmProtocol() { + @Inject + public AdmProtocol(Config config) { setSupportedDataCommands( Command.TYPE_GET_DEVICE_STATUS, Command.TYPE_CUSTOM); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new AdmFrameDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new AdmProtocolEncoder(AdmProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/AdmProtocolDecoder.java b/src/main/java/org/traccar/protocol/AdmProtocolDecoder.java index 7e3478704..1f940f7e2 100644 --- a/src/main/java/org/traccar/protocol/AdmProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/AdmProtocolDecoder.java @@ -18,7 +18,7 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.BitUtil; import org.traccar.helper.UnitsConverter; diff --git a/src/main/java/org/traccar/protocol/AisProtocol.java b/src/main/java/org/traccar/protocol/AisProtocol.java index 3b9cad7c8..e792a1d3f 100644 --- a/src/main/java/org/traccar/protocol/AisProtocol.java +++ b/src/main/java/org/traccar/protocol/AisProtocol.java @@ -19,13 +19,17 @@ import io.netty.handler.codec.string.StringDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class AisProtocol extends BaseProtocol { - public AisProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public AisProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringDecoder()); pipeline.addLast(new AisProtocolDecoder(AisProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/AisProtocolDecoder.java b/src/main/java/org/traccar/protocol/AisProtocolDecoder.java index 8970f3d4a..a434e6e33 100644 --- a/src/main/java/org/traccar/protocol/AisProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/AisProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.BitBuffer; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/AlematicsProtocol.java b/src/main/java/org/traccar/protocol/AlematicsProtocol.java index 8da2356b9..5219607e7 100644 --- a/src/main/java/org/traccar/protocol/AlematicsProtocol.java +++ b/src/main/java/org/traccar/protocol/AlematicsProtocol.java @@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class AlematicsProtocol extends BaseProtocol { - public AlematicsProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public AlematicsProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new AlematicsFrameDecoder(1024)); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/AlematicsProtocolDecoder.java b/src/main/java/org/traccar/protocol/AlematicsProtocolDecoder.java index 25ccf6856..981437191 100644 --- a/src/main/java/org/traccar/protocol/AlematicsProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/AlematicsProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.BitUtil; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/AnytrekProtocol.java b/src/main/java/org/traccar/protocol/AnytrekProtocol.java index 9bd0c9163..5dce15ce1 100644 --- a/src/main/java/org/traccar/protocol/AnytrekProtocol.java +++ b/src/main/java/org/traccar/protocol/AnytrekProtocol.java @@ -19,15 +19,19 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import java.nio.ByteOrder; +import jakarta.inject.Inject; + public class AnytrekProtocol extends BaseProtocol { - public AnytrekProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public AnytrekProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 2, 2, 2, 0, true)); pipeline.addLast(new AnytrekProtocolDecoder(AnytrekProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/AnytrekProtocolDecoder.java b/src/main/java/org/traccar/protocol/AnytrekProtocolDecoder.java index c48f59c90..0f9c2b17a 100644 --- a/src/main/java/org/traccar/protocol/AnytrekProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/AnytrekProtocolDecoder.java @@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/ApelProtocol.java b/src/main/java/org/traccar/protocol/ApelProtocol.java index 382aa16af..550581b85 100644 --- a/src/main/java/org/traccar/protocol/ApelProtocol.java +++ b/src/main/java/org/traccar/protocol/ApelProtocol.java @@ -19,14 +19,18 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import java.nio.ByteOrder; +import jakarta.inject.Inject; + public class ApelProtocol extends BaseProtocol { - public ApelProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public ApelProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 2, 2, 4, 0, true)); pipeline.addLast(new ApelProtocolDecoder(ApelProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/ApelProtocolDecoder.java b/src/main/java/org/traccar/protocol/ApelProtocolDecoder.java index c95a0366a..97ed7de96 100644 --- a/src/main/java/org/traccar/protocol/ApelProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/ApelProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.Checksum; diff --git a/src/main/java/org/traccar/protocol/AplicomProtocol.java b/src/main/java/org/traccar/protocol/AplicomProtocol.java index 2b9dbf97c..48c628d22 100644 --- a/src/main/java/org/traccar/protocol/AplicomProtocol.java +++ b/src/main/java/org/traccar/protocol/AplicomProtocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class AplicomProtocol extends BaseProtocol { - public AplicomProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public AplicomProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new AplicomFrameDecoder()); pipeline.addLast(new AplicomProtocolDecoder(AplicomProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/AplicomProtocolDecoder.java b/src/main/java/org/traccar/protocol/AplicomProtocolDecoder.java index f11312428..0cd8ca37e 100644 --- a/src/main/java/org/traccar/protocol/AplicomProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/AplicomProtocolDecoder.java @@ -21,8 +21,7 @@ import io.netty.channel.Channel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.config.Keys; import org.traccar.helper.Checksum; @@ -303,7 +302,7 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder { decodeEventData(position, buf, event); } - if (Context.getConfig().getBoolean(Keys.PROTOCOL_CAN.withPrefix(getProtocolName())) + if (getConfig().getBoolean(Keys.PROTOCOL_CAN.withPrefix(getProtocolName())) && buf.isReadable() && (selector & 0x1000) != 0 && event == EVENT_DATA) { decodeCanData(buf, position); } diff --git a/src/main/java/org/traccar/protocol/AppelloProtocol.java b/src/main/java/org/traccar/protocol/AppelloProtocol.java index 1ca4168e4..34055d7e4 100644 --- a/src/main/java/org/traccar/protocol/AppelloProtocol.java +++ b/src/main/java/org/traccar/protocol/AppelloProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class AppelloProtocol extends BaseProtocol { - public AppelloProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public AppelloProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/AppelloProtocolDecoder.java b/src/main/java/org/traccar/protocol/AppelloProtocolDecoder.java index 47e329234..8e182b9fb 100644 --- a/src/main/java/org/traccar/protocol/AppelloProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/AppelloProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/AquilaProtocol.java b/src/main/java/org/traccar/protocol/AquilaProtocol.java index 5ca1ec091..bd9c34d08 100644 --- a/src/main/java/org/traccar/protocol/AquilaProtocol.java +++ b/src/main/java/org/traccar/protocol/AquilaProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class AquilaProtocol extends BaseProtocol { - public AquilaProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public AquilaProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/AquilaProtocolDecoder.java b/src/main/java/org/traccar/protocol/AquilaProtocolDecoder.java index 3c43ddf2a..50ff10469 100644 --- a/src/main/java/org/traccar/protocol/AquilaProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/AquilaProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/Ardi01Protocol.java b/src/main/java/org/traccar/protocol/Ardi01Protocol.java index f7826430f..5ddbe9d62 100644 --- a/src/main/java/org/traccar/protocol/Ardi01Protocol.java +++ b/src/main/java/org/traccar/protocol/Ardi01Protocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class Ardi01Protocol extends BaseProtocol { - public Ardi01Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public Ardi01Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/Ardi01ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Ardi01ProtocolDecoder.java index 85e9ecfde..07653623a 100644 --- a/src/main/java/org/traccar/protocol/Ardi01ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Ardi01ProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/ArknavProtocol.java b/src/main/java/org/traccar/protocol/ArknavProtocol.java index 3b485e4a5..20fec296c 100644 --- a/src/main/java/org/traccar/protocol/ArknavProtocol.java +++ b/src/main/java/org/traccar/protocol/ArknavProtocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,19 +21,32 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class ArknavProtocol extends BaseProtocol { - public ArknavProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public ArknavProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '\r')); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new ArknavProtocolDecoder(ArknavProtocol.this)); } }); + addServer(new TrackerServer(config, getName(), true) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(new StringDecoder()); + pipeline.addLast(new StringEncoder()); + pipeline.addLast(new ArknavProtocolDecoder(ArknavProtocol.this)); + } + }); + } } diff --git a/src/main/java/org/traccar/protocol/ArknavProtocolDecoder.java b/src/main/java/org/traccar/protocol/ArknavProtocolDecoder.java index 4982e02fc..4def9c979 100644 --- a/src/main/java/org/traccar/protocol/ArknavProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/ArknavProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/ArknavX8Protocol.java b/src/main/java/org/traccar/protocol/ArknavX8Protocol.java index a29bc1ad3..a8be6f40c 100644 --- a/src/main/java/org/traccar/protocol/ArknavX8Protocol.java +++ b/src/main/java/org/traccar/protocol/ArknavX8Protocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class ArknavX8Protocol extends BaseProtocol { - public ArknavX8Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public ArknavX8Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ';')); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/ArknavX8ProtocolDecoder.java b/src/main/java/org/traccar/protocol/ArknavX8ProtocolDecoder.java index b570f5423..22c0344d6 100644 --- a/src/main/java/org/traccar/protocol/ArknavX8ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/ArknavX8ProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/ArmoliProtocol.java b/src/main/java/org/traccar/protocol/ArmoliProtocol.java index 5f36012af..e9c947ecd 100644 --- a/src/main/java/org/traccar/protocol/ArmoliProtocol.java +++ b/src/main/java/org/traccar/protocol/ArmoliProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class ArmoliProtocol extends BaseProtocol { - public ArmoliProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public ArmoliProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ";;", ";\r", ";")); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/ArmoliProtocolDecoder.java b/src/main/java/org/traccar/protocol/ArmoliProtocolDecoder.java index 50af039d6..cbed64f76 100644 --- a/src/main/java/org/traccar/protocol/ArmoliProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/ArmoliProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.ObdDecoder; diff --git a/src/main/java/org/traccar/protocol/ArnaviBinaryProtocolDecoder.java b/src/main/java/org/traccar/protocol/ArnaviBinaryProtocolDecoder.java index e957a6911..0f6b7a33f 100644 --- a/src/main/java/org/traccar/protocol/ArnaviBinaryProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/ArnaviBinaryProtocolDecoder.java @@ -20,7 +20,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.Checksum; diff --git a/src/main/java/org/traccar/protocol/ArnaviProtocol.java b/src/main/java/org/traccar/protocol/ArnaviProtocol.java index aecb42c8c..962bcce52 100644 --- a/src/main/java/org/traccar/protocol/ArnaviProtocol.java +++ b/src/main/java/org/traccar/protocol/ArnaviProtocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class ArnaviProtocol extends BaseProtocol { - public ArnaviProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public ArnaviProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new ArnaviFrameDecoder()); pipeline.addLast(new ArnaviProtocolDecoder(ArnaviProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java b/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java index 68a70c944..50ceea898 100644 --- a/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2020 - 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. @@ -15,11 +15,13 @@ */ package org.traccar.protocol; +import com.google.inject.Injector; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; import org.traccar.Protocol; +import jakarta.inject.Inject; import java.net.SocketAddress; public class ArnaviProtocolDecoder extends BaseProtocolDecoder { @@ -33,6 +35,12 @@ public class ArnaviProtocolDecoder extends BaseProtocolDecoder { binaryProtocolDecoder = new ArnaviBinaryProtocolDecoder(protocol); } + @Inject + public void setInjector(Injector injector) { + injector.injectMembers(textProtocolDecoder); + injector.injectMembers(binaryProtocolDecoder); + } + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { diff --git a/src/main/java/org/traccar/protocol/ArnaviTextProtocolDecoder.java b/src/main/java/org/traccar/protocol/ArnaviTextProtocolDecoder.java index b99869e6e..9d82c9ad5 100644 --- a/src/main/java/org/traccar/protocol/ArnaviTextProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/ArnaviTextProtocolDecoder.java @@ -18,7 +18,7 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/AstraProtocol.java b/src/main/java/org/traccar/protocol/AstraProtocol.java index 12b0dfb68..dcc02d10d 100644 --- a/src/main/java/org/traccar/protocol/AstraProtocol.java +++ b/src/main/java/org/traccar/protocol/AstraProtocol.java @@ -19,20 +19,24 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class AstraProtocol extends BaseProtocol { - public AstraProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public AstraProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 1, 2, -3, 0)); pipeline.addLast(new AstraProtocolDecoder(AstraProtocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new AstraProtocolDecoder(AstraProtocol.this)); } }); diff --git a/src/main/java/org/traccar/protocol/AstraProtocolDecoder.java b/src/main/java/org/traccar/protocol/AstraProtocolDecoder.java index e6f546b9f..366bf9e8b 100644 --- a/src/main/java/org/traccar/protocol/AstraProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/AstraProtocolDecoder.java @@ -21,7 +21,7 @@ import io.netty.channel.Channel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/At2000Protocol.java b/src/main/java/org/traccar/protocol/At2000Protocol.java index 5894f3eab..c7e22f142 100644 --- a/src/main/java/org/traccar/protocol/At2000Protocol.java +++ b/src/main/java/org/traccar/protocol/At2000Protocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class At2000Protocol extends BaseProtocol { - public At2000Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public At2000Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new At2000FrameDecoder()); pipeline.addLast(new At2000ProtocolDecoder(At2000Protocol.this)); } diff --git a/src/main/java/org/traccar/protocol/At2000ProtocolDecoder.java b/src/main/java/org/traccar/protocol/At2000ProtocolDecoder.java index 43798eb67..b81ba306d 100644 --- a/src/main/java/org/traccar/protocol/At2000ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/At2000ProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.DataConverter; diff --git a/src/main/java/org/traccar/protocol/AtrackProtocol.java b/src/main/java/org/traccar/protocol/AtrackProtocol.java index 429708b26..8b86955f4 100644 --- a/src/main/java/org/traccar/protocol/AtrackProtocol.java +++ b/src/main/java/org/traccar/protocol/AtrackProtocol.java @@ -18,24 +18,28 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class AtrackProtocol extends BaseProtocol { - public AtrackProtocol() { + @Inject + public AtrackProtocol(Config config) { setSupportedDataCommands( Command.TYPE_CUSTOM); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new AtrackFrameDecoder()); pipeline.addLast(new AtrackProtocolEncoder(AtrackProtocol.this)); pipeline.addLast(new AtrackProtocolDecoder(AtrackProtocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new AtrackProtocolEncoder(AtrackProtocol.this)); pipeline.addLast(new AtrackProtocolDecoder(AtrackProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java index 186b81470..8896dcfb0 100644 --- a/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java @@ -20,8 +20,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.config.Keys; @@ -54,7 +53,7 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder { private static final int MIN_DATA_LENGTH = 40; private boolean longDate; - private final boolean decimalFuel; + private boolean decimalFuel; private boolean custom; private String form; @@ -64,17 +63,20 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder { public AtrackProtocolDecoder(Protocol protocol) { super(protocol); + } - longDate = Context.getConfig().getBoolean(Keys.PROTOCOL_LONG_DATE.withPrefix(getProtocolName())); - decimalFuel = Context.getConfig().getBoolean(Keys.PROTOCOL_DECIMAL_FUEL.withPrefix(getProtocolName())); + @Override + protected void init() { + longDate = getConfig().getBoolean(Keys.PROTOCOL_LONG_DATE.withPrefix(getProtocolName())); + decimalFuel = getConfig().getBoolean(Keys.PROTOCOL_DECIMAL_FUEL.withPrefix(getProtocolName())); - custom = Context.getConfig().getBoolean(Keys.PROTOCOL_CUSTOM.withPrefix(getProtocolName())); - form = Context.getConfig().getString(Keys.PROTOCOL_FORM.withPrefix(getProtocolName())); + custom = getConfig().getBoolean(Keys.PROTOCOL_CUSTOM.withPrefix(getProtocolName())); + form = getConfig().getString(Keys.PROTOCOL_FORM.withPrefix(getProtocolName())); if (form != null) { custom = true; } - String alarmMapString = Context.getConfig().getString(Keys.PROTOCOL_ALARM_MAP.withPrefix(getProtocolName())); + String alarmMapString = getConfig().getString(Keys.PROTOCOL_ALARM_MAP.withPrefix(getProtocolName())); if (alarmMapString != null) { for (String pair : alarmMapString.split(",")) { if (!pair.isEmpty()) { @@ -98,7 +100,7 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder { this.form = form; } - private static void sendResponse(Channel channel, SocketAddress remoteAddress, long rawId, int index) { + private void sendResponse(Channel channel, SocketAddress remoteAddress, long rawId, int index) { if (channel != null) { ByteBuf response = Unpooled.buffer(12); response.writeShort(0xfe02); @@ -427,6 +429,208 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder { case "MP": buf.readUnsignedByte(); // manifold absolute pressure break; + case "EO": + position.set(Position.KEY_ODOMETER, UnitsConverter.metersFromMiles(buf.readUnsignedInt())); + break; + case "EH": + position.set(Position.KEY_HOURS, buf.readUnsignedInt() * 360000); + break; + case "ZO1": + buf.readUnsignedByte(); // brake stroke status + break; + case "ZO2": + buf.readUnsignedByte(); // warning indicator status + break; + case "ZO3": + buf.readUnsignedByte(); // abs control status + break; + case "ZO4": + position.set(Position.KEY_THROTTLE, buf.readUnsignedByte() * 0.4); + break; + case "ZO5": + buf.readUnsignedByte(); // parking brake status + break; + case "ZO6": + position.set(Position.KEY_OBD_SPEED, buf.readUnsignedByte() * 0.805); + break; + case "ZO7": + buf.readUnsignedByte(); // cruise control status + break; + case "ZO8": + buf.readUnsignedByte(); // accelector pedal position + break; + case "ZO9": + position.set(Position.KEY_ENGINE_LOAD, buf.readUnsignedByte() * 0.5); + break; + case "ZO10": + position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedByte() * 0.5); + break; + case "ZO11": + buf.readUnsignedByte(); // engine oil pressure + break; + case "ZO12": + buf.readUnsignedByte(); // boost pressure + break; + case "ZO13": + buf.readUnsignedByte(); // intake temperature + break; + case "ZO14": + position.set(Position.KEY_COOLANT_TEMP, buf.readUnsignedByte()); + break; + case "ZO15": + buf.readUnsignedByte(); // brake application pressure + break; + case "ZO16": + buf.readUnsignedByte(); // brake primary pressure + break; + case "ZO17": + buf.readUnsignedByte(); // brake secondary pressure + break; + case "ZH1": + buf.readUnsignedShort(); // cargo weight + break; + case "ZH2": + position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedShort() * 16.428 / 3600); + break; + case "ZH3": + position.set(Position.KEY_RPM, buf.readUnsignedShort() * 0.25); + break; + case "ZL1": + buf.readUnsignedInt(); // fuel used (natural gas) + break; + case "ZL2": + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 161); + break; + case "ZL3": + buf.readUnsignedInt(); // vehicle hours + break; + case "ZL4": + position.set(Position.KEY_HOURS, buf.readUnsignedInt() * 5 * 36000); + break; + case "ZS1": + position.set(Position.KEY_VIN, readString(buf)); + break; + case "JO1": + buf.readUnsignedByte(); // pedals + break; + case "JO2": + buf.readUnsignedByte(); // power takeoff device + break; + case "JO3": + buf.readUnsignedByte(); // accelector pedal position + break; + case "JO4": + position.set(Position.KEY_ENGINE_LOAD, buf.readUnsignedByte()); + break; + case "JO5": + position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedByte() * 0.4); + break; + case "JO6": + buf.readUnsignedByte(); // fms vehicle interface + break; + case "JO7": + buf.readUnsignedByte(); // driver 2 + break; + case "JO8": + buf.readUnsignedByte(); // driver 1 + break; + case "JO9": + buf.readUnsignedByte(); // drivers + break; + case "JO10": + buf.readUnsignedByte(); // system information + break; + case "JO11": + position.set(Position.KEY_COOLANT_TEMP, buf.readUnsignedByte() - 40); + break; + case "JO12": + buf.readUnsignedByte(); // pto engaged + break; + case "JH1": + position.set(Position.KEY_OBD_SPEED, buf.readUnsignedShort() / 256.0); + break; + case "JH2": + position.set(Position.KEY_RPM, buf.readUnsignedShort() * 0.125); + break; + case "JH3": + case "JH4": + case "JH5": + case "JH6": + case "JH7": + int index = Integer.parseInt(key.substring(2)) - 2; + position.set("axleWeight" + index, buf.readUnsignedShort() * 0.5); + break; + case "JH8": + position.set(Position.KEY_ODOMETER_SERVICE, buf.readUnsignedShort() * 5); + break; + case "JH9": + buf.readUnsignedShort(); // tachograph speed + break; + case "JH10": + buf.readUnsignedShort(); // ambient air temperature + break; + case "JH11": + position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedShort() * 0.05); + break; + case "JH12": + buf.readUnsignedShort(); // fuel economy + break; + case "JL1": + position.set(Position.KEY_FUEL_USED, buf.readUnsignedInt() * 0.5); + break; + case "JL2": + position.set(Position.KEY_HOURS, buf.readUnsignedInt() * 5 * 36000); + break; + case "JL3": + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 1000); + break; + case "JL4": + position.set(Position.KEY_FUEL_USED, buf.readUnsignedInt() * 0.001); + break; + case "JS1": + position.set(Position.KEY_VIN, readString(buf)); + break; + case "JS2": + readString(buf); // fms version supported + break; + case "JS3": + position.set("driver1", readString(buf)); + break; + case "JS4": + position.set("driver2", readString(buf)); + break; + case "JN1": + buf.readUnsignedInt(); // cruise control distance + break; + case "JN2": + buf.readUnsignedInt(); // excessive idling time + break; + case "JN3": + buf.readUnsignedInt(); // excessive idling fuel + break; + case "JN4": + buf.readUnsignedInt(); // pto time + break; + case "JN5": + buf.readUnsignedInt(); // pto fuel + break; + case "IN0": + position.set(Position.KEY_IGNITION, buf.readUnsignedByte() > 0); + break; + case "IN1": + case "IN2": + case "IN3": + position.set(Position.PREFIX_IN + key.charAt(2), buf.readUnsignedByte() > 0); + break; + case "HA": + position.set(Position.KEY_ALARM, buf.readUnsignedByte() > 0 ? Position.ALARM_ACCELERATION : null); + break; + case "HB": + position.set(Position.KEY_ALARM, buf.readUnsignedByte() > 0 ? Position.ALARM_BRAKING : null); + break; + case "HC": + position.set(Position.KEY_ALARM, buf.readUnsignedByte() > 0 ? Position.ALARM_CORNERING : null); + break; default: break; } @@ -524,20 +728,24 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder { private List<Position> decodeText(Channel channel, SocketAddress remoteAddress, String sentence) { - int startIndex = -1; - for (int i = 0; i < 4; i++) { - startIndex = sentence.indexOf(',', startIndex + 1); + int positionIndex = -1; + for (int i = 0; i < 5; i++) { + positionIndex = sentence.indexOf(',', positionIndex + 1); } - int endIndex = sentence.indexOf(',', startIndex + 1); - String imei = sentence.substring(startIndex + 1, endIndex); - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei); + String[] headers = sentence.substring(0, positionIndex).split(","); + long id = Long.parseLong(headers[2]); + int index = Integer.parseInt(headers[3]); + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, headers[4]); if (deviceSession == null) { return null; } + sendResponse(channel, remoteAddress, id, index); + List<Position> positions = new LinkedList<>(); - String[] lines = sentence.substring(endIndex + 1).split("\r\n"); + String[] lines = sentence.substring(positionIndex + 1).split("\r\n"); for (String line : lines) { Position position = decodeTextLine(deviceSession, line); @@ -626,7 +834,7 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder { getLastLocation(position, new Date(time * 1000)); - position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(String.valueOf(id), photo, "jpg")); + position.set(Position.KEY_IMAGE, writeMediaFile(String.valueOf(id), photo, "jpg")); photo.release(); photo = null; diff --git a/src/main/java/org/traccar/protocol/AuroProtocol.java b/src/main/java/org/traccar/protocol/AuroProtocol.java index b8ebdaa75..728c8e23c 100644 --- a/src/main/java/org/traccar/protocol/AuroProtocol.java +++ b/src/main/java/org/traccar/protocol/AuroProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class AuroProtocol extends BaseProtocol { - public AuroProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public AuroProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/AuroProtocolDecoder.java b/src/main/java/org/traccar/protocol/AuroProtocolDecoder.java index d7916147b..4489cf27e 100644 --- a/src/main/java/org/traccar/protocol/AuroProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/AuroProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/AustinNbProtocol.java b/src/main/java/org/traccar/protocol/AustinNbProtocol.java index 32bfc0aae..467deff53 100644 --- a/src/main/java/org/traccar/protocol/AustinNbProtocol.java +++ b/src/main/java/org/traccar/protocol/AustinNbProtocol.java @@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class AustinNbProtocol extends BaseProtocol { - public AustinNbProtocol() { - addServer(new TrackerServer(true, getName()) { + @Inject + public AustinNbProtocol(Config config) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new AustinNbProtocolDecoder(AustinNbProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/AustinNbProtocolDecoder.java b/src/main/java/org/traccar/protocol/AustinNbProtocolDecoder.java index dc6f3d280..b07b94e20 100644 --- a/src/main/java/org/traccar/protocol/AustinNbProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/AustinNbProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,14 +17,13 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; import org.traccar.model.Position; import java.net.SocketAddress; -import java.util.TimeZone; import java.util.regex.Pattern; public class AustinNbProtocolDecoder extends BaseProtocolDecoder { @@ -64,7 +63,7 @@ public class AustinNbProtocolDecoder extends BaseProtocolDecoder { Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); - position.setTime(parser.nextDateTime(Parser.DateTimeFormat.YMD_HMS, TimeZone.getDefault().getID())); + position.setTime(parser.nextDateTime()); position.setValid(true); position.setLatitude(Double.parseDouble(parser.next().replace(',', '.'))); diff --git a/src/main/java/org/traccar/protocol/AutoFonProtocol.java b/src/main/java/org/traccar/protocol/AutoFonProtocol.java index 08b5edc7d..75bd28d5e 100644 --- a/src/main/java/org/traccar/protocol/AutoFonProtocol.java +++ b/src/main/java/org/traccar/protocol/AutoFonProtocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class AutoFonProtocol extends BaseProtocol { - public AutoFonProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public AutoFonProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new AutoFonFrameDecoder()); pipeline.addLast(new AutoFonProtocolDecoder(AutoFonProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/AutoFonProtocolDecoder.java b/src/main/java/org/traccar/protocol/AutoFonProtocolDecoder.java index aa05ca2d7..dd6a0e33c 100644 --- a/src/main/java/org/traccar/protocol/AutoFonProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/AutoFonProtocolDecoder.java @@ -21,7 +21,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/AutoGradeProtocol.java b/src/main/java/org/traccar/protocol/AutoGradeProtocol.java index c6dbb681e..69d9fb4e6 100644 --- a/src/main/java/org/traccar/protocol/AutoGradeProtocol.java +++ b/src/main/java/org/traccar/protocol/AutoGradeProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class AutoGradeProtocol extends BaseProtocol { - public AutoGradeProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public AutoGradeProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ')')); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/AutoGradeProtocolDecoder.java b/src/main/java/org/traccar/protocol/AutoGradeProtocolDecoder.java index 5052450b5..f52ac81c9 100644 --- a/src/main/java/org/traccar/protocol/AutoGradeProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/AutoGradeProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.BitUtil; import org.traccar.helper.DateBuilder; diff --git a/src/main/java/org/traccar/protocol/AutoTrackProtocol.java b/src/main/java/org/traccar/protocol/AutoTrackProtocol.java index 6aa7558bf..df489de3c 100644 --- a/src/main/java/org/traccar/protocol/AutoTrackProtocol.java +++ b/src/main/java/org/traccar/protocol/AutoTrackProtocol.java @@ -19,14 +19,18 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import java.nio.ByteOrder; +import jakarta.inject.Inject; + public class AutoTrackProtocol extends BaseProtocol { - public AutoTrackProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public AutoTrackProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 5, 2, 2, 0, true)); pipeline.addLast(new AutoTrackProtocolDecoder(AutoTrackProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/AutoTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/AutoTrackProtocolDecoder.java index da7f6b5a6..c072e55d0 100644 --- a/src/main/java/org/traccar/protocol/AutoTrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/AutoTrackProtocolDecoder.java @@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.Checksum; diff --git a/src/main/java/org/traccar/protocol/AvemaProtocol.java b/src/main/java/org/traccar/protocol/AvemaProtocol.java index dbfab4dea..0eeee41b8 100644 --- a/src/main/java/org/traccar/protocol/AvemaProtocol.java +++ b/src/main/java/org/traccar/protocol/AvemaProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class AvemaProtocol extends BaseProtocol { - public AvemaProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public AvemaProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/AvemaProtocolDecoder.java b/src/main/java/org/traccar/protocol/AvemaProtocolDecoder.java index 37836ad5f..0793975df 100644 --- a/src/main/java/org/traccar/protocol/AvemaProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/AvemaProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/Avl301Protocol.java b/src/main/java/org/traccar/protocol/Avl301Protocol.java index 71fc7cb26..452bc1501 100644 --- a/src/main/java/org/traccar/protocol/Avl301Protocol.java +++ b/src/main/java/org/traccar/protocol/Avl301Protocol.java @@ -19,13 +19,17 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class Avl301Protocol extends BaseProtocol { - public Avl301Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public Avl301Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(256, 2, 1, -3, 0)); pipeline.addLast(new Avl301ProtocolDecoder(Avl301Protocol.this)); } diff --git a/src/main/java/org/traccar/protocol/Avl301ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Avl301ProtocolDecoder.java index f6b7db2d6..8f036fc29 100644 --- a/src/main/java/org/traccar/protocol/Avl301ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Avl301ProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; @@ -125,7 +125,7 @@ public class Avl301ProtocolDecoder extends BaseProtocolDecoder { } position.setNetwork(new Network( - CellTower.fromLacCid(buf.readUnsignedShort(), buf.readUnsignedMedium()))); + CellTower.fromLacCid(getConfig(), buf.readUnsignedShort(), buf.readUnsignedMedium()))); position.set(Position.KEY_ALARM, Position.ALARM_GENERAL); int flags = buf.readUnsignedByte(); diff --git a/src/main/java/org/traccar/protocol/B2316Protocol.java b/src/main/java/org/traccar/protocol/B2316Protocol.java index 7f08870ce..e0a6821d8 100644 --- a/src/main/java/org/traccar/protocol/B2316Protocol.java +++ b/src/main/java/org/traccar/protocol/B2316Protocol.java @@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class B2316Protocol extends BaseProtocol { - public B2316Protocol() { - addServer(new TrackerServer(true, getName()) { + @Inject + public B2316Protocol(Config config) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new B2316ProtocolDecoder(B2316Protocol.this)); diff --git a/src/main/java/org/traccar/protocol/B2316ProtocolDecoder.java b/src/main/java/org/traccar/protocol/B2316ProtocolDecoder.java index 854107a20..b0a5411f7 100644 --- a/src/main/java/org/traccar/protocol/B2316ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/B2316ProtocolDecoder.java @@ -17,16 +17,16 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.model.CellTower; import org.traccar.model.Network; import org.traccar.model.Position; import org.traccar.model.WifiAccessPoint; -import javax.json.Json; -import javax.json.JsonArray; -import javax.json.JsonObject; +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; import java.io.StringReader; import java.net.SocketAddress; import java.util.Date; @@ -116,8 +116,9 @@ public class B2316ProtocolDecoder extends BaseProtocolDecoder { String[] points = item.getString("wi").split(";"); for (String point : points) { String[] values = point.split(","); + String mac = values[0].replaceAll("(..)", "$1:"); network.addWifiAccessPoint(WifiAccessPoint.from( - values[0].replaceAll("(..)", "$1:"), Integer.parseInt(values[1]))); + mac.substring(0, mac.length() - 1), Integer.parseInt(values[1]))); } } diff --git a/src/main/java/org/traccar/protocol/BceProtocol.java b/src/main/java/org/traccar/protocol/BceProtocol.java index c5e1dd04c..5e1c10abc 100644 --- a/src/main/java/org/traccar/protocol/BceProtocol.java +++ b/src/main/java/org/traccar/protocol/BceProtocol.java @@ -18,16 +18,20 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class BceProtocol extends BaseProtocol { - public BceProtocol() { + @Inject + public BceProtocol(Config config) { setSupportedDataCommands( Command.TYPE_OUTPUT_CONTROL); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new BceFrameDecoder()); pipeline.addLast(new BceProtocolEncoder(BceProtocol.this)); pipeline.addLast(new BceProtocolDecoder(BceProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/BceProtocolDecoder.java b/src/main/java/org/traccar/protocol/BceProtocolDecoder.java index 535827f3c..2c9459584 100644 --- a/src/main/java/org/traccar/protocol/BceProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/BceProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/BlackKiteProtocol.java b/src/main/java/org/traccar/protocol/BlackKiteProtocol.java index 617a24d7a..d584af5a1 100644 --- a/src/main/java/org/traccar/protocol/BlackKiteProtocol.java +++ b/src/main/java/org/traccar/protocol/BlackKiteProtocol.java @@ -19,13 +19,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class BlackKiteProtocol extends BaseProtocol { - public BlackKiteProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public BlackKiteProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new GalileoFrameDecoder()); pipeline.addLast(new BlackKiteProtocolDecoder(BlackKiteProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/BlackKiteProtocolDecoder.java b/src/main/java/org/traccar/protocol/BlackKiteProtocolDecoder.java index 474ceabdc..64fc439c4 100644 --- a/src/main/java/org/traccar/protocol/BlackKiteProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/BlackKiteProtocolDecoder.java @@ -20,7 +20,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/BlueProtocol.java b/src/main/java/org/traccar/protocol/BlueProtocol.java index d5dc5c421..821111ad7 100644 --- a/src/main/java/org/traccar/protocol/BlueProtocol.java +++ b/src/main/java/org/traccar/protocol/BlueProtocol.java @@ -19,13 +19,17 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class BlueProtocol extends BaseProtocol { - public BlueProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public BlueProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 1, 2, -2, 0)); pipeline.addLast(new BlueProtocolDecoder(BlueProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/BlueProtocolDecoder.java b/src/main/java/org/traccar/protocol/BlueProtocolDecoder.java index f35ac6fbe..db59c564d 100644 --- a/src/main/java/org/traccar/protocol/BlueProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/BlueProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/BoxProtocol.java b/src/main/java/org/traccar/protocol/BoxProtocol.java index dfea15938..ac1ba7cae 100644 --- a/src/main/java/org/traccar/protocol/BoxProtocol.java +++ b/src/main/java/org/traccar/protocol/BoxProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class BoxProtocol extends BaseProtocol { - public BoxProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public BoxProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '\r')); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/BoxProtocolDecoder.java b/src/main/java/org/traccar/protocol/BoxProtocolDecoder.java index 853fa8f81..8e92b69fb 100644 --- a/src/main/java/org/traccar/protocol/BoxProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/BoxProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/BstplProtocol.java b/src/main/java/org/traccar/protocol/BstplProtocol.java new file mode 100644 index 000000000..ccedcf3d9 --- /dev/null +++ b/src/main/java/org/traccar/protocol/BstplProtocol.java @@ -0,0 +1,43 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.handler.codec.string.StringDecoder; +import io.netty.handler.codec.string.StringEncoder; +import org.traccar.BaseProtocol; +import org.traccar.CharacterDelimiterFrameDecoder; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; + +public class BstplProtocol extends BaseProtocol { + + @Inject + public BstplProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '#')); + pipeline.addLast(new StringEncoder()); + pipeline.addLast(new StringDecoder()); + pipeline.addLast(new BstplProtocolDecoder(BstplProtocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/BstplProtocolDecoder.java b/src/main/java/org/traccar/protocol/BstplProtocolDecoder.java new file mode 100644 index 000000000..15c114642 --- /dev/null +++ b/src/main/java/org/traccar/protocol/BstplProtocolDecoder.java @@ -0,0 +1,137 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.Protocol; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; +import org.traccar.helper.UnitsConverter; +import org.traccar.model.Position; +import org.traccar.session.DeviceSession; + +import java.net.SocketAddress; +import java.util.regex.Pattern; + +public class BstplProtocolDecoder extends BaseProtocolDecoder { + + public BstplProtocolDecoder(Protocol protocol) { + super(protocol); + } + + private static final Pattern PATTERN = new PatternBuilder() + .text("BSTPL$") // header + .number("(d),") // type + .expression("([^,]+),") // device id + .expression("([AV]),") // validity + .number("(dd)(dd)(dd),") // date (ddmmyy) + .number("(dd)(dd)(dd),") // time (hhmmss) + .number("(d+.d+),([0NS]),") // latitude + .number("(d+.d+),([0EW]),") // longitude + .number("(d+),") // speed + .number("(d+),") // odometer + .number("(d+),") // course + .number("(d+),") // satellites + .number("([01]),") // box open + .number("(d+),") // rssi + .number("([01]),") // charge + .number("([01]),") // ignition + .number("([01]),") // engine + .number("([01]),") // locked + .number("(d+.d+),") // adc + .number("d+,") // reserved + .number("(d+.d+),") // battery + .expression("([^,]+),") // firmware + .number("([^,]+),") // iccid + .number("(d+.d+)") // power + .compile(); + + private String decodeAlarm(int value) { + switch (value) { + case 4: + return Position.ALARM_LOW_BATTERY; + case 5: + return Position.ALARM_ACCELERATION; + case 6: + return Position.ALARM_BRAKING; + case 7: + return Position.ALARM_OVERSPEED; + case 9: + return Position.ALARM_SOS; + default: + return null; + } + } + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + Parser parser = new Parser(PATTERN, (String) msg); + if (!parser.matches()) { + return null; + } + + int type = parser.nextInt(); + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.set(Position.KEY_ALARM, decodeAlarm(type)); + + position.setValid(parser.next().equals("A")); + position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS)); + position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM)); + position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM)); + position.setSpeed(UnitsConverter.knotsFromKph(parser.nextInt())); + + position.set(Position.KEY_ODOMETER, parser.nextInt() * 1000L); + + position.setCourse(parser.nextInt()); + + position.set(Position.KEY_SATELLITES, parser.nextInt()); + + boolean boxOpen = parser.nextInt() > 0; + if (type == 8 && boxOpen) { + position.set(Position.KEY_ALARM, Position.ALARM_TAMPERING); + } + position.set("boxOpen", boxOpen); + + position.set(Position.KEY_RSSI, parser.nextInt()); + + boolean charge = parser.nextInt() > 0; + if (type == 3) { + position.set(Position.KEY_ALARM, charge ? Position.ALARM_POWER_RESTORED : Position.ALARM_POWER_CUT); + } + position.set(Position.KEY_CHARGE, charge); + + position.set(Position.KEY_IGNITION, parser.nextInt() > 0); + position.set("engine", parser.nextInt() > 0); + position.set(Position.KEY_BLOCKED, parser.nextInt() > 0); + position.set(Position.PREFIX_ADC + 1, parser.nextDouble()); + position.set(Position.KEY_BATTERY, parser.nextDouble()); + position.set(Position.KEY_ICCID, parser.next()); + position.set(Position.KEY_POWER, parser.nextDouble()); + + return position; + } + +} diff --git a/src/main/java/org/traccar/protocol/C2stekProtocol.java b/src/main/java/org/traccar/protocol/C2stekProtocol.java index 804621fd3..5370ea762 100644 --- a/src/main/java/org/traccar/protocol/C2stekProtocol.java +++ b/src/main/java/org/traccar/protocol/C2stekProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class C2stekProtocol extends BaseProtocol { - public C2stekProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public C2stekProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, false, "$AP")); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/C2stekProtocolDecoder.java b/src/main/java/org/traccar/protocol/C2stekProtocolDecoder.java index e735c67a1..aef158fc7 100644 --- a/src/main/java/org/traccar/protocol/C2stekProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/C2stekProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.Parser; @@ -49,10 +49,12 @@ public class C2stekProtocolDecoder extends BaseProtocolDecoder { .number("(-?d+.d+)#") // altitude .number("(d+)#") // battery .number("d+#") // geo area alarm - .number("(x+)#") // alarm - .number("([01])?") // armed + .number("(x+)") // alarm + .groupBegin() + .number("#([01])?") // armed .number("([01])") // door .number("([01])#") // ignition + .groupEnd("?") .any() .text("$AP") .compile(); @@ -114,8 +116,10 @@ public class C2stekProtocolDecoder extends BaseProtocolDecoder { if (parser.hasNext()) { position.set(Position.KEY_ARMED, parser.nextInt() > 0); } - position.set(Position.KEY_DOOR, parser.nextInt() > 0); - position.set(Position.KEY_IGNITION, parser.nextInt() > 0); + if (parser.hasNext(2)) { + position.set(Position.KEY_DOOR, parser.nextInt() > 0); + position.set(Position.KEY_IGNITION, parser.nextInt() > 0); + } return position; } diff --git a/src/main/java/org/traccar/protocol/CalAmpProtocol.java b/src/main/java/org/traccar/protocol/CalAmpProtocol.java index 232e72a8c..06df6e196 100644 --- a/src/main/java/org/traccar/protocol/CalAmpProtocol.java +++ b/src/main/java/org/traccar/protocol/CalAmpProtocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class CalAmpProtocol extends BaseProtocol { - public CalAmpProtocol() { - addServer(new TrackerServer(true, getName()) { + @Inject + public CalAmpProtocol(Config config) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CalAmpProtocolDecoder(CalAmpProtocol.this)); } }); diff --git a/src/main/java/org/traccar/protocol/CalAmpProtocolDecoder.java b/src/main/java/org/traccar/protocol/CalAmpProtocolDecoder.java index 59b1fdf21..57f9c69ae 100644 --- a/src/main/java/org/traccar/protocol/CalAmpProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/CalAmpProtocolDecoder.java @@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/CarTrackProtocol.java b/src/main/java/org/traccar/protocol/CarTrackProtocol.java index e340fba25..366f32034 100644 --- a/src/main/java/org/traccar/protocol/CarTrackProtocol.java +++ b/src/main/java/org/traccar/protocol/CarTrackProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class CarTrackProtocol extends BaseProtocol { - public CarTrackProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public CarTrackProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "##")); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/CarTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/CarTrackProtocolDecoder.java index ce3345826..3f5418549 100644 --- a/src/main/java/org/traccar/protocol/CarTrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/CarTrackProtocolDecoder.java @@ -18,7 +18,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/CarcellProtocol.java b/src/main/java/org/traccar/protocol/CarcellProtocol.java index f08ab3bd9..7ae8159d5 100644 --- a/src/main/java/org/traccar/protocol/CarcellProtocol.java +++ b/src/main/java/org/traccar/protocol/CarcellProtocol.java @@ -21,17 +21,21 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class CarcellProtocol extends BaseProtocol { - public CarcellProtocol() { + @Inject + public CarcellProtocol(Config config) { setSupportedDataCommands( Command.TYPE_ENGINE_STOP, Command.TYPE_ENGINE_RESUME); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '\r')); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/CarcellProtocolDecoder.java b/src/main/java/org/traccar/protocol/CarcellProtocolDecoder.java index ec640ba71..54ae068fb 100644 --- a/src/main/java/org/traccar/protocol/CarcellProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/CarcellProtocolDecoder.java @@ -20,7 +20,7 @@ import java.util.regex.Pattern; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.Parser.CoordinateFormat; diff --git a/src/main/java/org/traccar/protocol/CarscopProtocol.java b/src/main/java/org/traccar/protocol/CarscopProtocol.java index 2c754a97f..e904c01c5 100644 --- a/src/main/java/org/traccar/protocol/CarscopProtocol.java +++ b/src/main/java/org/traccar/protocol/CarscopProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class CarscopProtocol extends BaseProtocol { - public CarscopProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public CarscopProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '^')); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/CarscopProtocolDecoder.java b/src/main/java/org/traccar/protocol/CarscopProtocolDecoder.java index 161666adc..f13a1d0eb 100644 --- a/src/main/java/org/traccar/protocol/CarscopProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/CarscopProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/CastelProtocol.java b/src/main/java/org/traccar/protocol/CastelProtocol.java index 44c52d68f..74a9e9ca1 100644 --- a/src/main/java/org/traccar/protocol/CastelProtocol.java +++ b/src/main/java/org/traccar/protocol/CastelProtocol.java @@ -19,26 +19,30 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; import java.nio.ByteOrder; +import jakarta.inject.Inject; + public class CastelProtocol extends BaseProtocol { - public CastelProtocol() { + @Inject + public CastelProtocol(Config config) { setSupportedDataCommands( Command.TYPE_ENGINE_STOP, Command.TYPE_ENGINE_RESUME); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 2, 2, -4, 0, true)); pipeline.addLast(new CastelProtocolEncoder(CastelProtocol.this)); pipeline.addLast(new CastelProtocolDecoder(CastelProtocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CastelProtocolEncoder(CastelProtocol.this)); pipeline.addLast(new CastelProtocolDecoder(CastelProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/CastelProtocolDecoder.java b/src/main/java/org/traccar/protocol/CastelProtocolDecoder.java index 23401b5ee..566d856bb 100644 --- a/src/main/java/org/traccar/protocol/CastelProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/CastelProtocolDecoder.java @@ -16,11 +16,10 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; @@ -160,6 +159,9 @@ public class CastelProtocolDecoder extends BaseProtocolDecoder { for (int i = 0; i < count; i++) { int value; + if (!PID_LENGTH_MAP.containsKey(pids[i])) { + throw new RuntimeException(String.format("Unknown PID 0x%02x", pids[i])); + } switch (PID_LENGTH_MAP.get(pids[i])) { case 1: value = buf.readUnsignedByte(); @@ -281,15 +283,27 @@ public class CastelProtocolDecoder extends BaseProtocolDecoder { case 0x0C: position.set(Position.KEY_ALARM, Position.ALARM_CORNERING); break; + case 0x0D: + position.set(Position.KEY_ALARM, Position.ALARM_FATIGUE_DRIVING); + break; case 0x0E: position.set(Position.KEY_ALARM, Position.ALARM_POWER_OFF); break; + case 0x11: + position.set(Position.KEY_ALARM, Position.ALARM_ACCIDENT); + break; + case 0x12: + position.set(Position.KEY_ALARM, Position.ALARM_TAMPERING); + break; case 0x16: position.set(Position.KEY_IGNITION, true); break; case 0x17: position.set(Position.KEY_IGNITION, false); break; + case 0x1C: + position.set(Position.KEY_ALARM, Position.ALARM_VIBRATION); + break; default: break; } @@ -356,9 +370,9 @@ public class CastelProtocolDecoder extends BaseProtocolDecoder { int alarmCount = buf.readUnsignedByte(); for (int i = 0; i < alarmCount; i++) { if (buf.readUnsignedByte() != 0) { - int alarm = buf.readUnsignedByte(); + int event = buf.readUnsignedByte(); for (Position p : positions) { - decodeAlarm(p, alarm); + decodeAlarm(p, event); } buf.readUnsignedShortLE(); // description buf.readUnsignedShortLE(); // threshold @@ -418,12 +432,25 @@ public class CastelProtocolDecoder extends BaseProtocolDecoder { return position; case MSG_SC_DTCS_PASSENGER: + case MSG_SC_DTCS_COMMERCIAL: position = createPosition(deviceSession); decodeStat(position, buf); buf.readUnsignedByte(); // flag - position.add(ObdDecoder.decodeCodes(ByteBufUtil.hexDump(buf.readSlice(buf.readUnsignedByte())))); + + count = buf.readUnsignedByte(); + StringBuilder codes = new StringBuilder(); + for (int i = 0; i < count; i++) { + if (type == MSG_SC_DTCS_COMMERCIAL) { + codes.append(ObdDecoder.decodeCode(buf.readUnsignedShortLE())); + buf.readUnsignedByte(); // attribute + buf.readUnsignedByte(); // occurrence + } else { + codes.append(ObdDecoder.decodeCode(buf.readUnsignedShortLE())); + } + } + position.set(Position.KEY_DTCS, codes.toString().trim()); return position; @@ -443,7 +470,7 @@ public class CastelProtocolDecoder extends BaseProtocolDecoder { decodeStat(position, buf); position.setNetwork(new Network( - CellTower.fromLacCid(buf.readUnsignedShortLE(), buf.readUnsignedShortLE()))); + CellTower.fromLacCid(getConfig(), buf.readUnsignedShortLE(), buf.readUnsignedShortLE()))); return position; @@ -499,7 +526,7 @@ public class CastelProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedByte(); // additional flags position.setNetwork(new Network( - CellTower.fromLacCid(buf.readUnsignedShortLE(), buf.readUnsignedShortLE()))); + CellTower.fromLacCid(getConfig(), buf.readUnsignedShortLE(), buf.readUnsignedShortLE()))); positions.add(position); } diff --git a/src/main/java/org/traccar/protocol/CastelProtocolEncoder.java b/src/main/java/org/traccar/protocol/CastelProtocolEncoder.java index dc694da28..61dde3e80 100644 --- a/src/main/java/org/traccar/protocol/CastelProtocolEncoder.java +++ b/src/main/java/org/traccar/protocol/CastelProtocolEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2018 - 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. @@ -18,10 +18,9 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import org.traccar.BaseProtocolEncoder; -import org.traccar.Context; +import org.traccar.Protocol; import org.traccar.helper.Checksum; import org.traccar.model.Command; -import org.traccar.Protocol; import java.nio.charset.StandardCharsets; @@ -34,7 +33,7 @@ public class CastelProtocolEncoder extends BaseProtocolEncoder { private ByteBuf encodeContent(long deviceId, short type, ByteBuf content) { ByteBuf buf = Unpooled.buffer(0); - String uniqueId = Context.getIdentityManager().getById(deviceId).getUniqueId(); + String uniqueId = getUniqueId(deviceId); buf.writeByte('@'); buf.writeByte('@'); diff --git a/src/main/java/org/traccar/protocol/CautelaProtocol.java b/src/main/java/org/traccar/protocol/CautelaProtocol.java index 452bdf8d4..067345f49 100644 --- a/src/main/java/org/traccar/protocol/CautelaProtocol.java +++ b/src/main/java/org/traccar/protocol/CautelaProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class CautelaProtocol extends BaseProtocol { - public CautelaProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public CautelaProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/CautelaProtocolDecoder.java b/src/main/java/org/traccar/protocol/CautelaProtocolDecoder.java index bddf19b41..37f733ac1 100644 --- a/src/main/java/org/traccar/protocol/CautelaProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/CautelaProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/CellocatorProtocol.java b/src/main/java/org/traccar/protocol/CellocatorProtocol.java index d910877cf..e3325c8b7 100644 --- a/src/main/java/org/traccar/protocol/CellocatorProtocol.java +++ b/src/main/java/org/traccar/protocol/CellocatorProtocol.java @@ -18,24 +18,28 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class CellocatorProtocol extends BaseProtocol { - public CellocatorProtocol() { + @Inject + public CellocatorProtocol(Config config) { setSupportedDataCommands( Command.TYPE_OUTPUT_CONTROL); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CellocatorFrameDecoder()); pipeline.addLast(new CellocatorProtocolEncoder(CellocatorProtocol.this)); pipeline.addLast(new CellocatorProtocolDecoder(CellocatorProtocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CellocatorProtocolEncoder(CellocatorProtocol.this)); pipeline.addLast(new CellocatorProtocolDecoder(CellocatorProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java b/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java index 09bd3572f..3573a95ca 100644 --- a/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 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. @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; @@ -120,13 +120,15 @@ public class CellocatorProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedByte(); // operator / configuration flags buf.readUnsignedByte(); // reason data - position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte())); + short event = buf.readUnsignedByte(); + position.set(Position.KEY_ALARM, decodeAlarm(event)); + position.set(Position.KEY_EVENT, event); position.set("mode", buf.readUnsignedByte()); - long input = buf.readUnsignedIntLE(); + long input = buf.readUnsignedInt(); + position.set(Position.KEY_IGNITION, BitUtil.check(input, 3 * 8 + 5)); position.set(Position.KEY_DOOR, BitUtil.check(input, 3 * 8)); - position.set(Position.KEY_IGNITION, BitUtil.check(input, 2 * 8 + 7)); position.set(Position.KEY_CHARGE, BitUtil.check(input, 7)); position.set(Position.KEY_INPUT, input); diff --git a/src/main/java/org/traccar/protocol/CguardProtocol.java b/src/main/java/org/traccar/protocol/CguardProtocol.java index 9157ca35c..c0fc9582a 100644 --- a/src/main/java/org/traccar/protocol/CguardProtocol.java +++ b/src/main/java/org/traccar/protocol/CguardProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class CguardProtocol extends BaseProtocol { - public CguardProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public CguardProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/CguardProtocolDecoder.java b/src/main/java/org/traccar/protocol/CguardProtocolDecoder.java index d934921f1..90f8e0caf 100644 --- a/src/main/java/org/traccar/protocol/CguardProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/CguardProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/CityeasyProtocol.java b/src/main/java/org/traccar/protocol/CityeasyProtocol.java index 8ab4ce93a..60ed5d135 100644 --- a/src/main/java/org/traccar/protocol/CityeasyProtocol.java +++ b/src/main/java/org/traccar/protocol/CityeasyProtocol.java @@ -19,19 +19,23 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class CityeasyProtocol extends BaseProtocol { - public CityeasyProtocol() { + @Inject + public CityeasyProtocol(Config config) { setSupportedDataCommands( Command.TYPE_POSITION_SINGLE, Command.TYPE_POSITION_PERIODIC, Command.TYPE_POSITION_STOP, Command.TYPE_SET_TIMEZONE); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 2, 2, -4, 0)); pipeline.addLast(new CityeasyProtocolEncoder(CityeasyProtocol.this)); pipeline.addLast(new CityeasyProtocolDecoder(CityeasyProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/CityeasyProtocolDecoder.java b/src/main/java/org/traccar/protocol/CityeasyProtocolDecoder.java index 9c4c7e11d..1b5eb55d4 100644 --- a/src/main/java/org/traccar/protocol/CityeasyProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/CityeasyProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Checksum; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/ContinentalProtocol.java b/src/main/java/org/traccar/protocol/ContinentalProtocol.java index bc7928fba..9567374fd 100644 --- a/src/main/java/org/traccar/protocol/ContinentalProtocol.java +++ b/src/main/java/org/traccar/protocol/ContinentalProtocol.java @@ -19,13 +19,17 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class ContinentalProtocol extends BaseProtocol { - public ContinentalProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public ContinentalProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 2, 2, -4, 0)); pipeline.addLast(new ContinentalProtocolDecoder(ContinentalProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/ContinentalProtocolDecoder.java b/src/main/java/org/traccar/protocol/ContinentalProtocolDecoder.java index 471afa0d6..280871e1e 100644 --- a/src/main/java/org/traccar/protocol/ContinentalProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/ContinentalProtocolDecoder.java @@ -18,7 +18,7 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.BitUtil; import org.traccar.helper.UnitsConverter; diff --git a/src/main/java/org/traccar/protocol/CradlepointProtocol.java b/src/main/java/org/traccar/protocol/CradlepointProtocol.java index 4a09e0311..220db0747 100644 --- a/src/main/java/org/traccar/protocol/CradlepointProtocol.java +++ b/src/main/java/org/traccar/protocol/CradlepointProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class CradlepointProtocol extends BaseProtocol { - public CradlepointProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public CradlepointProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/CradlepointProtocolDecoder.java b/src/main/java/org/traccar/protocol/CradlepointProtocolDecoder.java index a282131ce..924603291 100644 --- a/src/main/java/org/traccar/protocol/CradlepointProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/CradlepointProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/DingtekProtocol.java b/src/main/java/org/traccar/protocol/DingtekProtocol.java index cf2a6c0f5..ab3e32fdb 100644 --- a/src/main/java/org/traccar/protocol/DingtekProtocol.java +++ b/src/main/java/org/traccar/protocol/DingtekProtocol.java @@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class DingtekProtocol extends BaseProtocol { - public DingtekProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public DingtekProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new DingtekFrameDecoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/DingtekProtocolDecoder.java b/src/main/java/org/traccar/protocol/DingtekProtocolDecoder.java index 98fe4b7b3..580741ec9 100644 --- a/src/main/java/org/traccar/protocol/DingtekProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/DingtekProtocolDecoder.java @@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.DataConverter; import org.traccar.model.Position; diff --git a/src/main/java/org/traccar/protocol/DishaProtocol.java b/src/main/java/org/traccar/protocol/DishaProtocol.java index 38f49cc05..0a582731d 100644 --- a/src/main/java/org/traccar/protocol/DishaProtocol.java +++ b/src/main/java/org/traccar/protocol/DishaProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class DishaProtocol extends BaseProtocol { - public DishaProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public DishaProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/DishaProtocolDecoder.java b/src/main/java/org/traccar/protocol/DishaProtocolDecoder.java index 3223988ab..1327e7a6c 100644 --- a/src/main/java/org/traccar/protocol/DishaProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/DishaProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/DmtHttpProtocol.java b/src/main/java/org/traccar/protocol/DmtHttpProtocol.java index 34568128f..d15bfa1ca 100644 --- a/src/main/java/org/traccar/protocol/DmtHttpProtocol.java +++ b/src/main/java/org/traccar/protocol/DmtHttpProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class DmtHttpProtocol extends BaseProtocol { - public DmtHttpProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public DmtHttpProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new HttpResponseEncoder()); pipeline.addLast(new HttpRequestDecoder()); pipeline.addLast(new HttpObjectAggregator(65535)); diff --git a/src/main/java/org/traccar/protocol/DmtHttpProtocolDecoder.java b/src/main/java/org/traccar/protocol/DmtHttpProtocolDecoder.java index 815cce987..c2e617a2a 100644 --- a/src/main/java/org/traccar/protocol/DmtHttpProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/DmtHttpProtocolDecoder.java @@ -19,15 +19,15 @@ import io.netty.channel.Channel; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpResponseStatus; import org.traccar.BaseHttpProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.BitUtil; import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; -import javax.json.Json; -import javax.json.JsonArray; -import javax.json.JsonObject; +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; import java.io.StringReader; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; diff --git a/src/main/java/org/traccar/protocol/DmtProtocol.java b/src/main/java/org/traccar/protocol/DmtProtocol.java index 78a5243c0..e89920cd3 100644 --- a/src/main/java/org/traccar/protocol/DmtProtocol.java +++ b/src/main/java/org/traccar/protocol/DmtProtocol.java @@ -19,14 +19,18 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import java.nio.ByteOrder; +import jakarta.inject.Inject; + public class DmtProtocol extends BaseProtocol { - public DmtProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public DmtProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 3, 2, 0, 0, true)); pipeline.addLast(new DmtProtocolDecoder(DmtProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/DmtProtocolDecoder.java b/src/main/java/org/traccar/protocol/DmtProtocolDecoder.java index 96b06557a..320aa1b60 100644 --- a/src/main/java/org/traccar/protocol/DmtProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/DmtProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; @@ -184,9 +184,9 @@ public class DmtProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_IGNITION, BitUtil.check(input, 0)); - if (!BitUtil.check(input, 1)) { + if (!BitUtil.check(status, 1)) { position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY); - } else if (BitUtil.check(input, 6)) { + } else if (BitUtil.check(status, 6)) { position.set(Position.KEY_ALARM, Position.ALARM_TAMPERING); } diff --git a/src/main/java/org/traccar/protocol/DolphinProtocol.java b/src/main/java/org/traccar/protocol/DolphinProtocol.java index 07c827e18..e2acce7dd 100644 --- a/src/main/java/org/traccar/protocol/DolphinProtocol.java +++ b/src/main/java/org/traccar/protocol/DolphinProtocol.java @@ -19,15 +19,19 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import java.nio.ByteOrder; +import jakarta.inject.Inject; + public class DolphinProtocol extends BaseProtocol { - public DolphinProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public DolphinProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 4096, 20, 4, 4, 0, true)); pipeline.addLast(new DolphinProtocolDecoder(DolphinProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/DolphinProtocolDecoder.java b/src/main/java/org/traccar/protocol/DolphinProtocolDecoder.java index d509b3ec0..b43635a52 100644 --- a/src/main/java/org/traccar/protocol/DolphinProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/DolphinProtocolDecoder.java @@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.UnitsConverter; diff --git a/src/main/java/org/traccar/protocol/Dsf22Protocol.java b/src/main/java/org/traccar/protocol/Dsf22Protocol.java index bffc3e419..ad349a7ff 100644 --- a/src/main/java/org/traccar/protocol/Dsf22Protocol.java +++ b/src/main/java/org/traccar/protocol/Dsf22Protocol.java @@ -18,20 +18,24 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class Dsf22Protocol extends BaseProtocol { - public Dsf22Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public Dsf22Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new Dsf22FrameDecoder()); pipeline.addLast(new Dsf22ProtocolDecoder(Dsf22Protocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new Dsf22ProtocolDecoder(Dsf22Protocol.this)); } }); diff --git a/src/main/java/org/traccar/protocol/Dsf22ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Dsf22ProtocolDecoder.java index 3ef960f12..124bbfefa 100644 --- a/src/main/java/org/traccar/protocol/Dsf22ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Dsf22ProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/DualcamProtocol.java b/src/main/java/org/traccar/protocol/DualcamProtocol.java index 04c4f2bd1..4725cb180 100644 --- a/src/main/java/org/traccar/protocol/DualcamProtocol.java +++ b/src/main/java/org/traccar/protocol/DualcamProtocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class DualcamProtocol extends BaseProtocol { - public DualcamProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public DualcamProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new DualcamFrameDecoder()); pipeline.addLast(new DualcamProtocolDecoder(DualcamProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/DualcamProtocolDecoder.java b/src/main/java/org/traccar/protocol/DualcamProtocolDecoder.java index c64b8171f..d03f7648d 100644 --- a/src/main/java/org/traccar/protocol/DualcamProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/DualcamProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,8 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; @@ -47,7 +46,8 @@ public class DualcamProtocolDecoder extends BaseProtocolDecoder { private String uniqueId; private int packetCount; private int currentPacket; - private ByteBuf photo; + private boolean video; + private ByteBuf media; @Override protected Object decode( @@ -65,13 +65,26 @@ public class DualcamProtocolDecoder extends BaseProtocolDecoder { long settings = buf.readUnsignedInt(); if (channel != null && deviceSession != null) { ByteBuf response = Unpooled.buffer(); - if (BitUtil.between(settings, 26, 28) > 0) { + if (BitUtil.between(settings, 26, 30) > 0) { response.writeShort(MSG_FILE_REQUEST); - String file = BitUtil.check(settings, 26) ? "%photof" : "%photor"; + String file; + if (BitUtil.check(settings, 26)) { + video = false; + file = "%photof"; + } else if (BitUtil.check(settings, 27)) { + video = false; + file = "%photor"; + } else if (BitUtil.check(settings, 28)) { + video = true; + file = "%videof"; + } else { + video = true; + file = "%videor"; + } response.writeShort(file.length()); response.writeCharSequence(file, StandardCharsets.US_ASCII); } else { - response.writeShort(MSG_COMPLETE); + response.writeShort(MSG_INIT); } channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); } @@ -80,7 +93,7 @@ public class DualcamProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedShort(); // length packetCount = buf.readInt(); currentPacket = 1; - photo = Unpooled.buffer(); + media = Unpooled.buffer(); if (channel != null) { ByteBuf response = Unpooled.buffer(); response.writeShort(MSG_RESUME); @@ -91,17 +104,21 @@ public class DualcamProtocolDecoder extends BaseProtocolDecoder { break; case MSG_DATA: buf.readUnsignedShort(); // length - photo.writeBytes(buf, buf.readableBytes() - 2); + media.writeBytes(buf, buf.readableBytes() - 2); if (currentPacket == packetCount) { deviceSession = getDeviceSession(channel, remoteAddress); Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); getLastLocation(position, null); try { - position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(uniqueId, photo, "jpg")); + if (video) { + position.set(Position.KEY_VIDEO, writeMediaFile(uniqueId, media, "h265")); + } else { + position.set(Position.KEY_IMAGE, writeMediaFile(uniqueId, media, "jpg")); + } } finally { - photo.release(); - photo = null; + media.release(); + media = null; } if (channel != null) { ByteBuf response = Unpooled.buffer(); diff --git a/src/main/java/org/traccar/protocol/DwayProtocol.java b/src/main/java/org/traccar/protocol/DwayProtocol.java index 05fd8b6e7..2ba1cf5f1 100644 --- a/src/main/java/org/traccar/protocol/DwayProtocol.java +++ b/src/main/java/org/traccar/protocol/DwayProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class DwayProtocol extends BaseProtocol { - public DwayProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public DwayProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/DwayProtocolDecoder.java b/src/main/java/org/traccar/protocol/DwayProtocolDecoder.java index 9b02c898e..9cf40b011 100644 --- a/src/main/java/org/traccar/protocol/DwayProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/DwayProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/EasyTrackProtocol.java b/src/main/java/org/traccar/protocol/EasyTrackProtocol.java index 972b36077..25d4ef9a0 100644 --- a/src/main/java/org/traccar/protocol/EasyTrackProtocol.java +++ b/src/main/java/org/traccar/protocol/EasyTrackProtocol.java @@ -21,19 +21,23 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class EasyTrackProtocol extends BaseProtocol { - public EasyTrackProtocol() { + @Inject + public EasyTrackProtocol(Config config) { setSupportedDataCommands( Command.TYPE_ENGINE_STOP, Command.TYPE_ENGINE_RESUME, Command.TYPE_ALARM_ARM, Command.TYPE_ALARM_DISARM); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "#\r\n", "#", "\r\n")); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java index 4fcc48944..805cf1197 100644 --- a/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/EelinkProtocol.java b/src/main/java/org/traccar/protocol/EelinkProtocol.java index 8a055d643..2a3c0bd15 100644 --- a/src/main/java/org/traccar/protocol/EelinkProtocol.java +++ b/src/main/java/org/traccar/protocol/EelinkProtocol.java @@ -19,28 +19,32 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class EelinkProtocol extends BaseProtocol { - public EelinkProtocol() { + @Inject + public EelinkProtocol(Config config) { setSupportedDataCommands( Command.TYPE_CUSTOM, Command.TYPE_POSITION_SINGLE, Command.TYPE_ENGINE_STOP, Command.TYPE_ENGINE_RESUME, Command.TYPE_REBOOT_DEVICE); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 3, 2)); pipeline.addLast(new EelinkProtocolEncoder(EelinkProtocol.this, false)); pipeline.addLast(new EelinkProtocolDecoder(EelinkProtocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new EelinkProtocolEncoder(EelinkProtocol.this, true)); pipeline.addLast(new EelinkProtocolDecoder(EelinkProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java b/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java index 592e5a56c..db1b365c3 100644 --- a/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java @@ -21,7 +21,7 @@ import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.socket.DatagramChannel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; @@ -273,6 +273,17 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder { if (BitUtil.check(status, 1)) { position.set(Position.KEY_IGNITION, BitUtil.check(status, 2)); } + if (BitUtil.check(status, 3)) { + position.set(Position.KEY_ARMED, BitUtil.check(status, 4)); + position.set(Position.KEY_MOTION, BitUtil.check(status, 9)); + } + if (BitUtil.check(status, 5)) { + position.set(Position.KEY_BLOCKED, BitUtil.check(status, 6)); + } + if (BitUtil.check(status, 7)) { + position.set(Position.KEY_CHARGE, BitUtil.check(status, 8)); + } + position.set(Position.KEY_GPS, BitUtil.check(status, 10)); position.set(Position.KEY_STATUS, status); } @@ -303,7 +314,7 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder { } if (buf.readableBytes() >= 12) { - position.set(Position.PREFIX_TEMP + 1, buf.readUnsignedShort() / 256.0); + position.set(Position.PREFIX_TEMP + 1, buf.readShort() / 256.0); position.set("humidity", buf.readUnsignedShort() * 0.1); position.set("illuminance", buf.readUnsignedInt() / 256.0); position.set("co2", buf.readUnsignedInt()); diff --git a/src/main/java/org/traccar/protocol/EgtsProtocol.java b/src/main/java/org/traccar/protocol/EgtsProtocol.java index 5d4638f37..5450d9f01 100644 --- a/src/main/java/org/traccar/protocol/EgtsProtocol.java +++ b/src/main/java/org/traccar/protocol/EgtsProtocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class EgtsProtocol extends BaseProtocol { - public EgtsProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public EgtsProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new EgtsFrameDecoder()); pipeline.addLast(new EgtsProtocolDecoder(EgtsProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/EgtsProtocolDecoder.java b/src/main/java/org/traccar/protocol/EgtsProtocolDecoder.java index e65ddb0ef..01d329580 100644 --- a/src/main/java/org/traccar/protocol/EgtsProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/EgtsProtocolDecoder.java @@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; @@ -291,7 +291,7 @@ public class EgtsProtocolDecoder extends BaseProtocolDecoder { if (serviceType == SERVICE_TELEDATA && position.getValid()) { if (useObjectIdAsDeviceId && objectId != 0L) { - deviceSession = getDeviceSession(channel, remoteAddress, true, String.valueOf(objectId)); + deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(objectId)); if (deviceSession != null) { position.setDeviceId(deviceSession.getDeviceId()); } diff --git a/src/main/java/org/traccar/protocol/EnforaProtocol.java b/src/main/java/org/traccar/protocol/EnforaProtocol.java index e462ab322..3b796db25 100644 --- a/src/main/java/org/traccar/protocol/EnforaProtocol.java +++ b/src/main/java/org/traccar/protocol/EnforaProtocol.java @@ -19,26 +19,30 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class EnforaProtocol extends BaseProtocol { - public EnforaProtocol() { + @Inject + public EnforaProtocol(Config config) { setSupportedDataCommands( Command.TYPE_CUSTOM, Command.TYPE_ENGINE_STOP, Command.TYPE_ENGINE_RESUME); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 0, 2, -2, 2)); pipeline.addLast(new EnforaProtocolEncoder(EnforaProtocol.this)); pipeline.addLast(new EnforaProtocolDecoder(EnforaProtocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new EnforaProtocolEncoder(EnforaProtocol.this)); pipeline.addLast(new EnforaProtocolDecoder(EnforaProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/EnforaProtocolDecoder.java b/src/main/java/org/traccar/protocol/EnforaProtocolDecoder.java index bfa7a116b..dd1c8017b 100644 --- a/src/main/java/org/traccar/protocol/EnforaProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/EnforaProtocolDecoder.java @@ -18,7 +18,7 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.BufferUtil; import org.traccar.helper.DateBuilder; diff --git a/src/main/java/org/traccar/protocol/EnnfuProtocol.java b/src/main/java/org/traccar/protocol/EnnfuProtocol.java index 7ef94d83f..a3ff943fa 100644 --- a/src/main/java/org/traccar/protocol/EnnfuProtocol.java +++ b/src/main/java/org/traccar/protocol/EnnfuProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class EnnfuProtocol extends BaseProtocol { - public EnnfuProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public EnnfuProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '$')); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/EnnfuProtocolDecoder.java b/src/main/java/org/traccar/protocol/EnnfuProtocolDecoder.java index 792ed1098..2198938e2 100644 --- a/src/main/java/org/traccar/protocol/EnnfuProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/EnnfuProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/EnvotechProtocol.java b/src/main/java/org/traccar/protocol/EnvotechProtocol.java index 8eb71ee6b..e432ac07c 100644 --- a/src/main/java/org/traccar/protocol/EnvotechProtocol.java +++ b/src/main/java/org/traccar/protocol/EnvotechProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class EnvotechProtocol extends BaseProtocol { - public EnvotechProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public EnvotechProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '#')); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/EnvotechProtocolDecoder.java b/src/main/java/org/traccar/protocol/EnvotechProtocolDecoder.java index 083d8a921..750ff2bda 100644 --- a/src/main/java/org/traccar/protocol/EnvotechProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/EnvotechProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; @@ -48,14 +48,15 @@ public class EnvotechProtocolDecoder extends BaseProtocolDecoder { .number("(ddd),") // battery .number("(xx)") // inputs .number("(xx),") // outputs - .number("(xxx)?,") // fuel + .number("(xxx)?") // fuel + .number("(xxx)?,") // weight .number("(x{8}),") // status .expression("[^']*'") .number("(dd)(dd)(dd)") // date (ddmmyy) .number("(dd)(dd)(dd)") // time (hhmmss) .number("(d)") // fix - .number("(dd)(dd)(d+)([NS])") // latitude - .number("(ddd)(dd)(d+)([EW])") // longitude + .number("(d+)(d{5})([NS])") // latitude + .number("(d+)(d{5})([EW])") // longitude .number("(ddd)") // speed .number("(ddd)") // course .any() @@ -72,7 +73,18 @@ public class EnvotechProtocolDecoder extends BaseProtocolDecoder { Position position = new Position(getProtocolName()); - position.set(Position.KEY_EVENT, parser.nextHexInt()); + int event = parser.nextHexInt(); + switch (event) { + case 0x60: + position.set(Position.KEY_ALARM, Position.ALARM_LOCK); + break; + case 0x61: + position.set(Position.KEY_ALARM, Position.ALARM_UNLOCK); + break; + default: + break; + } + position.set(Position.KEY_EVENT, event); DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); if (deviceSession == null) { @@ -88,12 +100,13 @@ public class EnvotechProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_INPUT, parser.nextHexInt()); position.set(Position.PREFIX_OUT, parser.nextHexInt()); position.set(Position.KEY_FUEL_LEVEL, parser.nextHexInt()); - position.set(Position.KEY_STATUS, parser.nextHexInt()); + position.set("weight", parser.nextHexInt()); + position.set(Position.KEY_STATUS, parser.nextHexLong()); position.setFixTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS)); position.setValid(parser.nextInt() > 0); - position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_MIN_MIN_HEM)); - position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_MIN_MIN_HEM)); + position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_DEG_HEM)); + position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_DEG_HEM)); position.setSpeed(parser.nextInt()); position.setCourse(parser.nextInt()); diff --git a/src/main/java/org/traccar/protocol/EsealProtocol.java b/src/main/java/org/traccar/protocol/EsealProtocol.java index fc1d342e1..eae4cf2aa 100644 --- a/src/main/java/org/traccar/protocol/EsealProtocol.java +++ b/src/main/java/org/traccar/protocol/EsealProtocol.java @@ -21,18 +21,22 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class EsealProtocol extends BaseProtocol { - public EsealProtocol() { + @Inject + public EsealProtocol(Config config) { setSupportedDataCommands( Command.TYPE_CUSTOM, Command.TYPE_ALARM_ARM, Command.TYPE_ALARM_DISARM); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/EsealProtocolDecoder.java b/src/main/java/org/traccar/protocol/EsealProtocolDecoder.java index 0a12f781d..dd15c4276 100644 --- a/src/main/java/org/traccar/protocol/EsealProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/EsealProtocolDecoder.java @@ -17,8 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.config.Keys; @@ -32,11 +31,15 @@ import java.util.regex.Pattern; public class EsealProtocolDecoder extends BaseProtocolDecoder { - private final String config; + private String config; public EsealProtocolDecoder(Protocol protocol) { super(protocol); - config = Context.getConfig().getString(Keys.PROTOCOL_CONFIG.withPrefix(getProtocolName())); + } + + @Override + protected void init() { + config = getConfig().getString(Keys.PROTOCOL_CONFIG.withPrefix(getProtocolName())); } private static final Pattern PATTERN = new PatternBuilder() diff --git a/src/main/java/org/traccar/protocol/EskyProtocol.java b/src/main/java/org/traccar/protocol/EskyProtocol.java index fb047c207..002e268ba 100644 --- a/src/main/java/org/traccar/protocol/EskyProtocol.java +++ b/src/main/java/org/traccar/protocol/EskyProtocol.java @@ -20,22 +20,26 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class EskyProtocol extends BaseProtocol { - public EskyProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public EskyProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new EskyFrameDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new EskyProtocolDecoder(EskyProtocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new EskyProtocolDecoder(EskyProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/EskyProtocolDecoder.java b/src/main/java/org/traccar/protocol/EskyProtocolDecoder.java index 14b4376d5..4239022d0 100644 --- a/src/main/java/org/traccar/protocol/EskyProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/EskyProtocolDecoder.java @@ -18,7 +18,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import io.netty.channel.socket.DatagramChannel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/ExtremTracProtocol.java b/src/main/java/org/traccar/protocol/ExtremTracProtocol.java index 692fd4e99..23a993fe4 100644 --- a/src/main/java/org/traccar/protocol/ExtremTracProtocol.java +++ b/src/main/java/org/traccar/protocol/ExtremTracProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class ExtremTracProtocol extends BaseProtocol { - public ExtremTracProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public ExtremTracProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/ExtremTracProtocolDecoder.java b/src/main/java/org/traccar/protocol/ExtremTracProtocolDecoder.java index 9fde6f0a0..706c70825 100644 --- a/src/main/java/org/traccar/protocol/ExtremTracProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/ExtremTracProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/FifotrackProtocol.java b/src/main/java/org/traccar/protocol/FifotrackProtocol.java index 4a0a12ed3..e98ad84cc 100644 --- a/src/main/java/org/traccar/protocol/FifotrackProtocol.java +++ b/src/main/java/org/traccar/protocol/FifotrackProtocol.java @@ -19,17 +19,21 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class FifotrackProtocol extends BaseProtocol { - public FifotrackProtocol() { + @Inject + public FifotrackProtocol(Config config) { setSupportedDataCommands( Command.TYPE_CUSTOM, Command.TYPE_REQUEST_PHOTO); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new FifotrackFrameDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new FifotrackProtocolEncoder(FifotrackProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java index 5f9326a61..87587aa1e 100644 --- a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java @@ -19,8 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; @@ -35,6 +34,10 @@ import org.traccar.model.WifiAccessPoint; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; import java.util.regex.Pattern; public class FifotrackProtocolDecoder extends BaseProtocolDecoder { @@ -61,7 +64,7 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder { .number("(d+),") // course .number("(-?d+),") // altitude .number("(d+),") // odometer - .number("d+,") // runtime + .number("(d+),") // engine hours .number("(x+),") // status .number("(x+)?,") // input .number("(x+)?,") // output @@ -79,7 +82,7 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder { .text("$$") .number("d+,") // length .number("(d+),") // imei - .number("x+,") // index + .number("(x+),") // index .text("A03,") // type .number("(d+)?,") // alarm .number("(dd)(dd)(dd)") // date (yymmdd) @@ -138,16 +141,20 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder { .number("xx") .compile(); - private void requestPhoto(Channel channel, SocketAddress socketAddress, String imei, String file) { + private void sendResponse(Channel channel, SocketAddress remoteAddress, String imei, String content) { if (channel != null) { - String content = "1,D06," + file + "," + photo.writerIndex() + "," + Math.min(1024, photo.writableBytes()); int length = 1 + imei.length() + 1 + content.length(); String response = String.format("##%02d,%s,%s*", length, imei, content); response += Checksum.sum(response) + "\r\n"; - channel.writeAndFlush(new NetworkMessage(response, socketAddress)); + channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); } } + private void requestPhoto(Channel channel, SocketAddress remoteAddress, String imei, String file) { + String content = "1,D06," + file + "," + photo.writerIndex() + "," + Math.min(1024, photo.writableBytes()); + sendResponse(channel, remoteAddress, imei, content); + } + private String decodeAlarm(Integer alarm) { if (alarm != null) { switch (alarm) { @@ -201,11 +208,14 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder { return null; } - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + String imei = parser.next(); + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei); if (deviceSession == null) { return null; } + String index = parser.next(); + Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); @@ -225,13 +235,15 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder { position.setValid(parser.next().equals("A")); position.setFixTime(position.getDeviceTime()); - position.set(Position.KEY_SATELLITES, parser.nextInt()); position.setSpeed(UnitsConverter.knotsFromKph(parser.nextInt())); + position.set(Position.KEY_SATELLITES, parser.nextInt()); position.setLatitude(parser.nextDouble()); position.setLongitude(parser.nextDouble()); } else { + getLastLocation(position, position.getDeviceTime()); + String[] points = parser.next().split("\\|"); for (String point : points) { String[] wifi = point.split(":"); @@ -244,6 +256,11 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder { position.setNetwork(network); + DateFormat dateFormat = new SimpleDateFormat("yyMMddHHmmss"); + dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + String response = index + ",A03," + dateFormat.format(new Date()); + sendResponse(channel, remoteAddress, imei, response); + return position; } @@ -275,6 +292,7 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder { position.setAltitude(parser.nextInt()); position.set(Position.KEY_ODOMETER, parser.nextLong()); + position.set(Position.KEY_HOURS, parser.nextLong() * 1000); long status = parser.nextHexLong(); position.set(Position.KEY_RSSI, BitUtil.between(status, 3, 8)); @@ -293,11 +311,11 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder { } if (parser.hasNext()) { - String rfid = parser.next(); - if (rfid.matches("\\p{XDigit}+")) { - position.set(Position.KEY_DRIVER_UNIQUE_ID, String.valueOf(Integer.parseInt(rfid, 16))); + String value = parser.next(); + if (value.matches("\\p{XDigit}+")) { + position.set(Position.KEY_DRIVER_UNIQUE_ID, String.valueOf(Integer.parseInt(value, 16))); } else { - position.set("driverLicense", rfid); + position.set(Position.KEY_CARD, value); } } @@ -381,7 +399,7 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder { Position position = new Position(getProtocolName()); position.setDeviceId(getDeviceSession(channel, remoteAddress, imei).getDeviceId()); getLastLocation(position, null); - position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(imei, photo, "jpg")); + position.set(Position.KEY_IMAGE, writeMediaFile(imei, photo, "jpg")); photo.release(); photo = null; return position; diff --git a/src/main/java/org/traccar/protocol/FlespiProtocol.java b/src/main/java/org/traccar/protocol/FlespiProtocol.java index 05b105f93..0d8448845 100644 --- a/src/main/java/org/traccar/protocol/FlespiProtocol.java +++ b/src/main/java/org/traccar/protocol/FlespiProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class FlespiProtocol extends BaseProtocol { - public FlespiProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public FlespiProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new HttpResponseEncoder()); pipeline.addLast(new HttpRequestDecoder(4096, 8192, 128 * 1024)); pipeline.addLast(new HttpObjectAggregator(Integer.MAX_VALUE)); diff --git a/src/main/java/org/traccar/protocol/FlespiProtocolDecoder.java b/src/main/java/org/traccar/protocol/FlespiProtocolDecoder.java index 83ca74ce5..ea076afd8 100644 --- a/src/main/java/org/traccar/protocol/FlespiProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/FlespiProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 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. @@ -19,17 +19,17 @@ import io.netty.channel.Channel; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpResponseStatus; import org.traccar.BaseHttpProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; -import javax.json.Json; -import javax.json.JsonArray; -import javax.json.JsonNumber; -import javax.json.JsonObject; -import javax.json.JsonString; -import javax.json.JsonValue; +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonNumber; +import jakarta.json.JsonObject; +import jakarta.json.JsonString; +import jakarta.json.JsonValue; import java.io.StringReader; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; @@ -54,11 +54,11 @@ public class FlespiProtocolDecoder extends BaseHttpProtocolDecoder { List<Position> positions = new LinkedList<>(); for (int i = 0; i < result.size(); i++) { JsonObject message = result.getJsonObject(i); - JsonString ident = message.getJsonString("ident"); - if (ident == null) { + JsonString identifier = message.getJsonString("ident"); + if (identifier == null) { continue; } - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, ident.getString()); + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, identifier.getString()); if (deviceSession == null) { continue; } @@ -125,12 +125,13 @@ public class FlespiProtocolDecoder extends BaseHttpProtocolDecoder { position.set(Position.KEY_PDOP, ((JsonNumber) value).doubleValue()); return true; case "din": + position.set(Position.KEY_INPUT, ((JsonNumber) value).intValue()); + return true; case "dout": - if (name.equals("din")) { - position.set(Position.KEY_INPUT, ((JsonNumber) value).intValue()); - } else { - position.set(Position.KEY_OUTPUT, ((JsonNumber) value).intValue()); - } + position.set(Position.KEY_OUTPUT, ((JsonNumber) value).intValue()); + return true; + case "report.reason": + position.set(Position.KEY_EVENT, ((JsonNumber) value).intValue()); return true; case "gps.vehicle.mileage": position.set(Position.KEY_ODOMETER, ((JsonNumber) value).doubleValue()); @@ -141,6 +142,9 @@ public class FlespiProtocolDecoder extends BaseHttpProtocolDecoder { case "battery.voltage": position.set(Position.KEY_BATTERY, ((JsonNumber) value).doubleValue()); return true; + case "battery.level": + position.set(Position.KEY_BATTERY_LEVEL, ((JsonNumber) value).intValue()); + return true; case "fuel.level": case "can.fuel.level": position.set(Position.KEY_FUEL_LEVEL, ((JsonNumber) value).doubleValue()); @@ -228,6 +232,15 @@ public class FlespiProtocolDecoder extends BaseHttpProtocolDecoder { position.set(Position.KEY_ALARM, Position.ALARM_BONNET); } return true; + case "custom.wln_accel_max": + position.set("maxAcceleration", ((JsonNumber) value).doubleValue()); + return true; + case "custom.wln_brk_max": + position.set("maxBraking", ((JsonNumber) value).doubleValue()); + return true; + case "custom.wln_crn_max": + position.set("maxCornering", ((JsonNumber) value).doubleValue()); + return true; default: return false; } diff --git a/src/main/java/org/traccar/protocol/FlexApiProtocol.java b/src/main/java/org/traccar/protocol/FlexApiProtocol.java index 7f4154f71..b2e3f5d00 100644 --- a/src/main/java/org/traccar/protocol/FlexApiProtocol.java +++ b/src/main/java/org/traccar/protocol/FlexApiProtocol.java @@ -20,15 +20,19 @@ import io.netty.handler.codec.string.StringDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import java.nio.charset.StandardCharsets; +import jakarta.inject.Inject; + public class FlexApiProtocol extends BaseProtocol { - public FlexApiProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public FlexApiProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(5120)); pipeline.addLast(new StringDecoder(StandardCharsets.US_ASCII)); pipeline.addLast(new FlexApiProtocolDecoder(FlexApiProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/FlexApiProtocolDecoder.java b/src/main/java/org/traccar/protocol/FlexApiProtocolDecoder.java index bcfbdd7da..fb76673ca 100644 --- a/src/main/java/org/traccar/protocol/FlexApiProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/FlexApiProtocolDecoder.java @@ -17,14 +17,14 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.model.CellTower; import org.traccar.model.Network; import org.traccar.model.Position; -import javax.json.Json; -import javax.json.JsonObject; +import jakarta.json.Json; +import jakarta.json.JsonObject; import java.io.StringReader; import java.net.SocketAddress; import java.util.Date; diff --git a/src/main/java/org/traccar/protocol/FlexCommProtocol.java b/src/main/java/org/traccar/protocol/FlexCommProtocol.java index 9343ebeb8..293b9b12b 100644 --- a/src/main/java/org/traccar/protocol/FlexCommProtocol.java +++ b/src/main/java/org/traccar/protocol/FlexCommProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class FlexCommProtocol extends BaseProtocol { - public FlexCommProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public FlexCommProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new FixedLengthFrameDecoder(2 + 2 + 101 + 5)); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/FlexCommProtocolDecoder.java b/src/main/java/org/traccar/protocol/FlexCommProtocolDecoder.java index 068c0a05c..0d8bd9373 100644 --- a/src/main/java/org/traccar/protocol/FlexCommProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/FlexCommProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/FlexibleReportProtocol.java b/src/main/java/org/traccar/protocol/FlexibleReportProtocol.java index 0cd55343a..a16e61458 100644 --- a/src/main/java/org/traccar/protocol/FlexibleReportProtocol.java +++ b/src/main/java/org/traccar/protocol/FlexibleReportProtocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class FlexibleReportProtocol extends BaseProtocol { - public FlexibleReportProtocol() { - addServer(new TrackerServer(true, getName()) { + @Inject + public FlexibleReportProtocol(Config config) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new FlexibleReportProtocolDecoder(FlexibleReportProtocol.this)); } }); diff --git a/src/main/java/org/traccar/protocol/FlexibleReportProtocolDecoder.java b/src/main/java/org/traccar/protocol/FlexibleReportProtocolDecoder.java index 759f2cd6f..9fcee1aeb 100644 --- a/src/main/java/org/traccar/protocol/FlexibleReportProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/FlexibleReportProtocolDecoder.java @@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/FlextrackProtocol.java b/src/main/java/org/traccar/protocol/FlextrackProtocol.java index ddd1d58f0..b6353de9d 100644 --- a/src/main/java/org/traccar/protocol/FlextrackProtocol.java +++ b/src/main/java/org/traccar/protocol/FlextrackProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class FlextrackProtocol extends BaseProtocol { - public FlextrackProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public FlextrackProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "\r")); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/FlextrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/FlextrackProtocolDecoder.java index 9dce22ede..a0dac1c41 100644 --- a/src/main/java/org/traccar/protocol/FlextrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/FlextrackProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/FoxProtocol.java b/src/main/java/org/traccar/protocol/FoxProtocol.java index 9bac773b5..edb496f11 100644 --- a/src/main/java/org/traccar/protocol/FoxProtocol.java +++ b/src/main/java/org/traccar/protocol/FoxProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class FoxProtocol extends BaseProtocol { - public FoxProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public FoxProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "</fox>")); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/FoxProtocolDecoder.java b/src/main/java/org/traccar/protocol/FoxProtocolDecoder.java index 449f00022..6dd0b0e95 100644 --- a/src/main/java/org/traccar/protocol/FoxProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/FoxProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/FreedomProtocol.java b/src/main/java/org/traccar/protocol/FreedomProtocol.java index bc6b92d5f..87404094a 100644 --- a/src/main/java/org/traccar/protocol/FreedomProtocol.java +++ b/src/main/java/org/traccar/protocol/FreedomProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class FreedomProtocol extends BaseProtocol { - public FreedomProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public FreedomProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/FreedomProtocolDecoder.java b/src/main/java/org/traccar/protocol/FreedomProtocolDecoder.java index 1d2dd3133..27dda1a6d 100644 --- a/src/main/java/org/traccar/protocol/FreedomProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/FreedomProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/FreematicsProtocol.java b/src/main/java/org/traccar/protocol/FreematicsProtocol.java index 999b075a1..c4076f330 100644 --- a/src/main/java/org/traccar/protocol/FreematicsProtocol.java +++ b/src/main/java/org/traccar/protocol/FreematicsProtocol.java @@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class FreematicsProtocol extends BaseProtocol { - public FreematicsProtocol() { - addServer(new TrackerServer(true, getName()) { + @Inject + public FreematicsProtocol(Config config) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new FreematicsProtocolDecoder(FreematicsProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/FreematicsProtocolDecoder.java b/src/main/java/org/traccar/protocol/FreematicsProtocolDecoder.java index aded35823..4d8e7e7ea 100644 --- a/src/main/java/org/traccar/protocol/FreematicsProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/FreematicsProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.Checksum; @@ -153,7 +153,7 @@ public class FreematicsProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_RSSI, Integer.parseInt(value)); break; case 0x82: - position.set(Position.KEY_DEVICE_TEMP, Integer.parseInt(value) * 0.1); + position.set(Position.KEY_DEVICE_TEMP, Double.parseDouble(value) * 0.1); break; case 0x104: position.set(Position.KEY_ENGINE_LOAD, Integer.parseInt(value)); diff --git a/src/main/java/org/traccar/protocol/FutureWayProtocol.java b/src/main/java/org/traccar/protocol/FutureWayProtocol.java index 73b53ee12..20586bede 100644 --- a/src/main/java/org/traccar/protocol/FutureWayProtocol.java +++ b/src/main/java/org/traccar/protocol/FutureWayProtocol.java @@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class FutureWayProtocol extends BaseProtocol { - public FutureWayProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public FutureWayProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new FutureWayFrameDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/FutureWayProtocolDecoder.java b/src/main/java/org/traccar/protocol/FutureWayProtocolDecoder.java index c2f3781d9..57027b080 100644 --- a/src/main/java/org/traccar/protocol/FutureWayProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/FutureWayProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.DataConverter; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/G1rusFrameDecoder.java b/src/main/java/org/traccar/protocol/G1rusFrameDecoder.java new file mode 100644 index 000000000..8c67207ad --- /dev/null +++ b/src/main/java/org/traccar/protocol/G1rusFrameDecoder.java @@ -0,0 +1,49 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import org.traccar.BaseFrameDecoder; + +public class G1rusFrameDecoder extends BaseFrameDecoder { + + @Override + protected Object decode( + ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception { + + ByteBuf result = Unpooled.buffer(); + + while (buf.isReadable()) { + int b = buf.readUnsignedByte(); + if (b == 0x1B) { + int ext = buf.readUnsignedByte(); + if (ext == 0x00) { + result.writeByte(0x1B); + } else { + result.writeByte(0xF8); + } + } else { + result.writeByte(b); + } + } + + return result; + } + +} diff --git a/src/main/java/org/traccar/protocol/G1rusProtocol.java b/src/main/java/org/traccar/protocol/G1rusProtocol.java new file mode 100644 index 000000000..b3904b357 --- /dev/null +++ b/src/main/java/org/traccar/protocol/G1rusProtocol.java @@ -0,0 +1,40 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.handler.codec.string.StringEncoder; +import org.traccar.BaseProtocol; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; + +public class G1rusProtocol extends BaseProtocol { + + @Inject + public G1rusProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(new G1rusFrameDecoder()); + pipeline.addLast(new StringEncoder()); + pipeline.addLast(new G1rusProtocolDecoder(G1rusProtocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/G1rusProtocolDecoder.java b/src/main/java/org/traccar/protocol/G1rusProtocolDecoder.java new file mode 100644 index 000000000..17cfbc1eb --- /dev/null +++ b/src/main/java/org/traccar/protocol/G1rusProtocolDecoder.java @@ -0,0 +1,165 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.Protocol; +import org.traccar.helper.BitUtil; +import org.traccar.model.Position; +import org.traccar.session.DeviceSession; + +import java.net.SocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +public class G1rusProtocolDecoder extends BaseProtocolDecoder { + public G1rusProtocolDecoder(Protocol protocol) { + super(protocol); + } + + public static final int MSG_HEARTBEAT = 0; + public static final int MSG_REGULAR = 1; + public static final int MSG_SMS_FORWARD = 2; + public static final int MSG_SERIAL = 3; + public static final int MSG_MIXED = 4; + + private String readString(ByteBuf buf) { + int length = buf.readUnsignedByte() & 0xF; + return buf.readCharSequence(length, StandardCharsets.US_ASCII).toString(); + } + + private Position decodeRegular(DeviceSession deviceSession, ByteBuf buf, int type) { + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + position.setTime(new Date((buf.readUnsignedIntLE() + 946684800) * 1000L)); + + if (BitUtil.check(type, 6)) { + position.set(Position.KEY_EVENT, buf.readUnsignedByte()); + } + + int dataMask = buf.readUnsignedShort(); + + if (BitUtil.check(dataMask, 0)) { + buf.readUnsignedByte(); // length + readString(buf); // device name + position.set(Position.KEY_VERSION_FW, readString(buf)); + position.set(Position.KEY_VERSION_HW, readString(buf)); + } + + if (BitUtil.check(dataMask, 1)) { + buf.readUnsignedByte(); // length + int locationMask = buf.readUnsignedShort(); + if (BitUtil.check(locationMask, 0)) { + int validity = buf.readUnsignedByte(); + position.set(Position.KEY_SATELLITES, BitUtil.to(validity, 5)); + position.setValid(BitUtil.between(validity, 5, 7) == 2); + } + if (BitUtil.check(locationMask, 1)) { + position.setLatitude(buf.readInt() / 1000000.0); + position.setLongitude(buf.readInt() / 1000000.0); + } + if (BitUtil.check(locationMask, 2)) { + position.setSpeed(buf.readUnsignedShort()); + } + if (BitUtil.check(locationMask, 3)) { + position.setCourse(buf.readUnsignedShort()); + } + if (BitUtil.check(locationMask, 4)) { + position.setAltitude(buf.readShort()); + } + if (BitUtil.check(locationMask, 5)) { + position.set(Position.KEY_HDOP, buf.readUnsignedShort()); + } + if (BitUtil.check(locationMask, 6)) { + position.set(Position.KEY_VDOP, buf.readUnsignedShort()); + } + } + + if (BitUtil.check(dataMask, 2)) { + buf.skipBytes(buf.readUnsignedByte()); + } + + if (BitUtil.check(dataMask, 3)) { + buf.skipBytes(buf.readUnsignedByte()); + } + + if (BitUtil.check(dataMask, 4)) { + buf.readUnsignedByte(); // length + position.set(Position.KEY_POWER, buf.readUnsignedShort() * 110 / 4096 - 10); + position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 110 / 4096 - 10); + position.set(Position.KEY_DEVICE_TEMP, buf.readUnsignedShort() * 110 / 4096 - 10); + } + + if (BitUtil.check(dataMask, 5)) { + buf.skipBytes(buf.readUnsignedByte()); + } + + if (BitUtil.check(dataMask, 7)) { + buf.skipBytes(buf.readUnsignedByte()); + } + + return position; + } + + @Override + protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + ByteBuf buf = (ByteBuf) msg; + + buf.readUnsignedByte(); // header + buf.readUnsignedByte(); // version + + int type = buf.readUnsignedByte(); + String imei = String.valueOf(buf.readLong()); + buf.readerIndex(buf.readerIndex() - 1); + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei); + if (deviceSession == null) { + return null; + } + + if (BitUtil.to(type, 6) == MSG_REGULAR) { + + return decodeRegular(deviceSession, buf, type); + + } else if (BitUtil.to(type, 6) == MSG_MIXED) { + + List<Position> positions = new LinkedList<>(); + while (buf.readableBytes() > 5) { + int length = buf.readUnsignedShort(); + int subtype = buf.readUnsignedByte(); + if (BitUtil.to(subtype, 6) == MSG_REGULAR) { + positions.add(decodeRegular(deviceSession, buf, subtype)); + } else { + buf.skipBytes(length - 1); + } + } + return positions.isEmpty() ? null : positions; + + } + + buf.readUnsignedShort(); // checksum + buf.readUnsignedByte(); // tail + + return null; + + } + +} diff --git a/src/main/java/org/traccar/protocol/GalileoFrameDecoder.java b/src/main/java/org/traccar/protocol/GalileoFrameDecoder.java index c23d26c83..d90e48287 100644 --- a/src/main/java/org/traccar/protocol/GalileoFrameDecoder.java +++ b/src/main/java/org/traccar/protocol/GalileoFrameDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 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. @@ -22,7 +22,7 @@ import org.traccar.BaseFrameDecoder; public class GalileoFrameDecoder extends BaseFrameDecoder { - private static final int MESSAGE_MINIMUM_LENGTH = 5; + private static final int MESSAGE_MINIMUM_LENGTH = 6; @Override protected Object decode( @@ -32,9 +32,15 @@ public class GalileoFrameDecoder extends BaseFrameDecoder { return null; } - int length = buf.getUnsignedShortLE(buf.readerIndex() + 1) & 0x7fff; - if (buf.readableBytes() >= (length + MESSAGE_MINIMUM_LENGTH)) { - return buf.readRetainedSlice(length + MESSAGE_MINIMUM_LENGTH); + int length; + if (buf.getByte(buf.readerIndex()) == 0x01 && buf.getUnsignedMedium(buf.readerIndex() + 3) == 0x01001c) { + length = 3 + buf.getUnsignedShort(buf.readerIndex() + 1); + } else { + length = 5 + (buf.getUnsignedShortLE(buf.readerIndex() + 1) & 0x7fff); + } + + if (buf.readableBytes() >= length) { + return buf.readRetainedSlice(length); } return null; diff --git a/src/main/java/org/traccar/protocol/GalileoProtocol.java b/src/main/java/org/traccar/protocol/GalileoProtocol.java index a1570c9b0..95ce4edde 100644 --- a/src/main/java/org/traccar/protocol/GalileoProtocol.java +++ b/src/main/java/org/traccar/protocol/GalileoProtocol.java @@ -18,17 +18,21 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class GalileoProtocol extends BaseProtocol { - public GalileoProtocol() { + @Inject + public GalileoProtocol(Config config) { setSupportedDataCommands( Command.TYPE_CUSTOM, Command.TYPE_OUTPUT_CONTROL); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new GalileoFrameDecoder()); pipeline.addLast(new GalileoProtocolEncoder(GalileoProtocol.this)); pipeline.addLast(new GalileoProtocolDecoder(GalileoProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java b/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java index f29fb9850..44baa94ea 100644 --- a/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 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. @@ -20,15 +20,19 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; +import org.traccar.config.Keys; +import org.traccar.helper.BitBuffer; +import org.traccar.helper.BitUtil; import org.traccar.helper.UnitsConverter; +import org.traccar.helper.model.AttributeUtil; import org.traccar.model.Position; +import org.traccar.session.DeviceSession; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; +import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -36,6 +40,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TimeZone; public class GalileoProtocolDecoder extends BaseProtocolDecoder { @@ -44,6 +49,17 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder { } private ByteBuf photo; + private boolean compressed; + + public void setCompressed(boolean compressed) { + this.compressed = compressed; + } + + public boolean getCompressed(long deviceId) { + Boolean value = AttributeUtil.lookup( + getCacheManager(), Keys.PROTOCOL_EXTENDED.withPrefix(getProtocolName()), deviceId); + return value != null ? value : compressed; + } private static final Map<Integer, Integer> TAG_LENGTH_MAP = new HashMap<>(); @@ -64,7 +80,7 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder { }; int[] l3 = { 0x63, 0x64, 0x6f, 0x5d, 0x65, 0x66, 0x67, 0x68, - 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e + 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0xfa }; int[] l4 = { 0x20, 0x33, 0x44, 0x90, 0xc0, 0xc2, 0xc3, 0xd3, @@ -86,6 +102,8 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder { } TAG_LENGTH_MAP.put(0x5b, 7); // variable length TAG_LENGTH_MAP.put(0x5c, 68); + TAG_LENGTH_MAP.put(0xfd, 8); + TAG_LENGTH_MAP.put(0xfe, 8); } private static int getTagLength(int tag) { @@ -215,7 +233,6 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder { break; case 0xea: position.set("userDataArray", ByteBufUtil.hexDump(buf.readSlice(buf.readUnsignedByte()))); - position.set("userDataArray", ByteBufUtil.hexDump(buf.readSlice(buf.readUnsignedByte()))); break; default: buf.skipBytes(getTagLength(tag)); @@ -231,18 +248,105 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder { int header = buf.readUnsignedByte(); if (header == 0x01) { - return decodePositions(channel, remoteAddress, buf); + if (buf.getUnsignedMedium(buf.readerIndex() + 2) == 0x01001c) { + return decodeIridiumPosition(channel, remoteAddress, buf); + } else { + return decodePositions(channel, remoteAddress, buf); + } } else if (header == 0x07) { return decodePhoto(channel, remoteAddress, buf); + } else if (header == 0x08) { + return decodeCompressedPositions(channel, remoteAddress, buf); } return null; } - private Object decodePositions( - Channel channel, SocketAddress remoteAddress, ByteBuf buf) throws Exception { + private void decodeMinimalDataSet(Position position, ByteBuf buf) { + BitBuffer bits = new BitBuffer(buf.readSlice(10)); + bits.readUnsigned(1); + + Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + calendar.set(Calendar.DAY_OF_YEAR, 1); + calendar.set(Calendar.HOUR_OF_DAY, calendar.getActualMinimum(Calendar.HOUR_OF_DAY)); + calendar.set(Calendar.MINUTE, calendar.getActualMinimum(Calendar.MINUTE)); + calendar.set(Calendar.SECOND, calendar.getActualMinimum(Calendar.SECOND)); + calendar.set(Calendar.MILLISECOND, calendar.getActualMinimum(Calendar.MILLISECOND)); + calendar.add(Calendar.SECOND, bits.readUnsigned(25)); + position.setTime(calendar.getTime()); + + position.setValid(bits.readUnsigned(1) == 0); + position.setLongitude(360 * bits.readUnsigned(22) / 4194304.0 - 180); + position.setLatitude(180 * bits.readUnsigned(21) / 2097152.0 - 90); + if (bits.readUnsigned(1) > 0) { + position.set(Position.KEY_ALARM, Position.ALARM_GENERAL); + } + } + + private Position decodeIridiumPosition(Channel channel, SocketAddress remoteAddress, ByteBuf buf) { + + buf.readUnsignedShort(); // length + + buf.skipBytes(3); // identification header + buf.readUnsignedInt(); // index - int length = (buf.readUnsignedShortLE() & 0x7fff) + 3; + DeviceSession deviceSession = getDeviceSession( + channel, remoteAddress, buf.readSlice(15).toString(StandardCharsets.US_ASCII)); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + buf.readUnsignedByte(); // session status + buf.skipBytes(4); // reserved + position.setTime(new Date(buf.readUnsignedInt() * 1000)); + + buf.skipBytes(3); // coordinates header + int flags = buf.readUnsignedByte(); + double latitude = buf.readUnsignedByte() + buf.readUnsignedShort() / 60000.0; + double longitude = buf.readUnsignedByte() + buf.readUnsignedShort() / 60000.0; + position.setLatitude(BitUtil.check(flags, 1) ? -latitude : latitude); + position.setLongitude(BitUtil.check(flags, 0) ? -longitude : longitude); + buf.readUnsignedInt(); // accuracy + + buf.readUnsignedByte(); // data tag header + ByteBuf data = buf.readSlice(buf.readUnsignedShort()); + if (getCompressed(deviceSession.getDeviceId())) { + + decodeMinimalDataSet(position, data); + + int[] tags = new int[BitUtil.to(data.readUnsignedByte(), 8)]; + for (int i = 0; i < tags.length; i++) { + tags[i] = data.readUnsignedByte(); + } + + for (int tag : tags) { + decodeTag(position, data, tag); + } + + } else { + + while (data.isReadable()) { + int tag = data.readUnsignedByte(); + if (tag == 0x30) { + position.setValid((data.readUnsignedByte() & 0xf0) == 0x00); + position.setLatitude(data.readIntLE() / 1000000.0); + position.setLongitude(data.readIntLE() / 1000000.0); + } else { + decodeTag(position, data, tag); + } + } + + } + + return position; + } + + private List<Position> decodePositions(Channel channel, SocketAddress remoteAddress, ByteBuf buf) { + + int endIndex = (buf.readUnsignedShortLE() & 0x7fff) + buf.readerIndex(); List<Position> positions = new LinkedList<>(); Set<Integer> tags = new HashSet<>(); @@ -251,7 +355,7 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder { DeviceSession deviceSession = null; Position position = new Position(getProtocolName()); - while (buf.readerIndex() < length) { + while (buf.readerIndex() < endIndex) { int tag = buf.readUnsignedByte(); if (tags.contains(tag)) { @@ -287,7 +391,7 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder { if (hasLocation && position.getFixTime() != null) { positions.add(position); - } else if (position.getAttributes().containsKey(Position.KEY_RESULT)) { + } else if (position.hasAttribute(Position.KEY_RESULT)) { position.setDeviceId(deviceSession.getDeviceId()); getLastLocation(position, null); positions.add(position); @@ -322,14 +426,13 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder { } else { DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); - String uniqueId = Context.getIdentityManager().getById(deviceSession.getDeviceId()).getUniqueId(); position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); getLastLocation(position, null); - position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(uniqueId, photo, "jpg")); + position.set(Position.KEY_IMAGE, writeMediaFile(deviceSession.getUniqueId(), photo, "jpg")); photo.release(); photo = null; @@ -340,4 +443,43 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder { return position; } + private List<Position> decodeCompressedPositions(Channel channel, SocketAddress remoteAddress, ByteBuf buf) { + + buf.readUnsignedShortLE(); // length + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); + if (deviceSession == null) { + return null; + } + + List<Position> positions = new LinkedList<>(); + while (buf.readableBytes() > 2) { + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + decodeMinimalDataSet(position, buf); + + int[] tags = new int[BitUtil.to(buf.readUnsignedByte(), 8)]; + for (int i = 0; i < tags.length; i++) { + tags[i] = buf.readUnsignedByte(); + } + + for (int tag : tags) { + decodeTag(position, buf, tag); + } + + positions.add(position); + + } + + sendResponse(channel, 0x02, buf.readUnsignedShortLE()); + + for (Position p : positions) { + p.setDeviceId(deviceSession.getDeviceId()); + } + + return positions.isEmpty() ? null : positions; + } + } diff --git a/src/main/java/org/traccar/protocol/GatorProtocol.java b/src/main/java/org/traccar/protocol/GatorProtocol.java index ca81caefb..c961093ab 100644 --- a/src/main/java/org/traccar/protocol/GatorProtocol.java +++ b/src/main/java/org/traccar/protocol/GatorProtocol.java @@ -19,20 +19,27 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; +import org.traccar.model.Command; + +import jakarta.inject.Inject; public class GatorProtocol extends BaseProtocol { - public GatorProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public GatorProtocol(Config config) { + setSupportedDataCommands(Command.TYPE_POSITION_SINGLE); + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 3, 2)); + pipeline.addLast(new GatorProtocolEncoder(GatorProtocol.this)); pipeline.addLast(new GatorProtocolDecoder(GatorProtocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new GatorProtocolDecoder(GatorProtocol.this)); } }); diff --git a/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java b/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java index 087861635..f7da5dc75 100644 --- a/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BcdUtil; @@ -37,6 +37,7 @@ public class GatorProtocolDecoder extends BaseProtocolDecoder { } public static final int MSG_HEARTBEAT = 0x21; + public static final int MSG_POSITION_REQUEST = 0x30; public static final int MSG_POSITION_DATA = 0x80; public static final int MSG_ROLLCALL_RESPONSE = 0x81; public static final int MSG_ALARM_DATA = 0x82; diff --git a/src/main/java/org/traccar/protocol/GatorProtocolEncoder.java b/src/main/java/org/traccar/protocol/GatorProtocolEncoder.java new file mode 100644 index 000000000..3d38b7455 --- /dev/null +++ b/src/main/java/org/traccar/protocol/GatorProtocolEncoder.java @@ -0,0 +1,76 @@ +/* + * Copyright 2023 Hossain Mohammad Seym (seym45@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import org.traccar.BaseProtocolEncoder; +import org.traccar.Protocol; +import org.traccar.helper.Checksum; +import org.traccar.model.Command; + +public class GatorProtocolEncoder extends BaseProtocolEncoder { + + public GatorProtocolEncoder(Protocol protocol) { + super(protocol); + } + + public ByteBuf encodeId(long deviceId) { + ByteBuf buf = Unpooled.buffer(); + + String id = getUniqueId(deviceId); + + int firstDigit = Integer.parseInt(id.substring(1, 3)) - 30; + + buf.writeByte(Integer.parseInt(id.substring(3, 5)) | (((firstDigit >> 3) & 1) << 7)); + buf.writeByte(Integer.parseInt(id.substring(5, 7)) | (((firstDigit >> 2) & 1) << 7)); + buf.writeByte(Integer.parseInt(id.substring(7, 9)) | (((firstDigit >> 1) & 1) << 7)); + buf.writeByte(Integer.parseInt(id.substring(9)) | ((firstDigit & 1) << 7)); + + return buf; + } + + private ByteBuf encodeContent(long deviceId, int type) { + ByteBuf buf = Unpooled.buffer(); + + buf.writeByte(0x24); + buf.writeByte(0x24); + buf.writeByte(type); + buf.writeByte(0x00); + buf.writeByte(4 + 1 + 1); // ip 4 bytes, checksum and end byte + + ByteBuf pseudoIPAddress = encodeId(deviceId); + buf.writeBytes(pseudoIPAddress); + + int checksum = Checksum.xor(buf.nioBuffer()); + buf.writeByte(checksum); + + buf.writeByte(0x0D); + + return buf; + } + + @Override + protected Object encodeCommand(Command command) { + + switch (command.getType()) { + case Command.TYPE_POSITION_SINGLE: + return encodeContent(command.getDeviceId(), GatorProtocolDecoder.MSG_POSITION_REQUEST); + default: + return null; + } + } +} diff --git a/src/main/java/org/traccar/protocol/GenxProtocol.java b/src/main/java/org/traccar/protocol/GenxProtocol.java index c87ba946a..7e5a6a69e 100644 --- a/src/main/java/org/traccar/protocol/GenxProtocol.java +++ b/src/main/java/org/traccar/protocol/GenxProtocol.java @@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class GenxProtocol extends BaseProtocol { - public GenxProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public GenxProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringDecoder()); pipeline.addLast(new GenxProtocolDecoder(GenxProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/GenxProtocolDecoder.java b/src/main/java/org/traccar/protocol/GenxProtocolDecoder.java index 2ae9de7a0..6448b6a5a 100644 --- a/src/main/java/org/traccar/protocol/GenxProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/GenxProtocolDecoder.java @@ -17,8 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; @@ -32,7 +31,11 @@ public class GenxProtocolDecoder extends BaseProtocolDecoder { public GenxProtocolDecoder(Protocol protocol) { super(protocol); - setReportColumns(Context.getConfig().getString(getProtocolName() + ".reportColumns", "1,2,3,4")); + } + + @Override + protected void init() { + setReportColumns(getConfig().getString(getProtocolName() + ".reportColumns", "1,2,3,4")); } public void setReportColumns(String format) { diff --git a/src/main/java/org/traccar/protocol/Gl100Protocol.java b/src/main/java/org/traccar/protocol/Gl100Protocol.java index 063e606db..fdd79648f 100644 --- a/src/main/java/org/traccar/protocol/Gl100Protocol.java +++ b/src/main/java/org/traccar/protocol/Gl100Protocol.java @@ -21,22 +21,26 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class Gl100Protocol extends BaseProtocol { - public Gl100Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public Gl100Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '\0')); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new Gl100ProtocolDecoder(Gl100Protocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new Gl100ProtocolDecoder(Gl100Protocol.this)); diff --git a/src/main/java/org/traccar/protocol/Gl100ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl100ProtocolDecoder.java index ae0383e5c..789d87dad 100644 --- a/src/main/java/org/traccar/protocol/Gl100ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gl100ProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/Gl200BinaryProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200BinaryProtocolDecoder.java index c3339bea5..ecd1f5bfa 100644 --- a/src/main/java/org/traccar/protocol/Gl200BinaryProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gl200BinaryProtocolDecoder.java @@ -18,7 +18,7 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.BitBuffer; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/Gl200Protocol.java b/src/main/java/org/traccar/protocol/Gl200Protocol.java index e2d0c6d2a..e3ddbb46e 100644 --- a/src/main/java/org/traccar/protocol/Gl200Protocol.java +++ b/src/main/java/org/traccar/protocol/Gl200Protocol.java @@ -15,34 +15,37 @@ */ package org.traccar.protocol; +import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; -import io.netty.handler.codec.string.StringEncoder; +import jakarta.inject.Inject; public class Gl200Protocol extends BaseProtocol { - public Gl200Protocol() { + @Inject + public Gl200Protocol(Config config) { setSupportedDataCommands( Command.TYPE_POSITION_SINGLE, Command.TYPE_ENGINE_STOP, Command.TYPE_ENGINE_RESUME, Command.TYPE_IDENTIFICATION, Command.TYPE_REBOOT_DEVICE); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new Gl200FrameDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new Gl200ProtocolEncoder(Gl200Protocol.this)); pipeline.addLast(new Gl200ProtocolDecoder(Gl200Protocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringEncoder()); pipeline.addLast(new Gl200ProtocolEncoder(Gl200Protocol.this)); pipeline.addLast(new Gl200ProtocolDecoder(Gl200Protocol.this)); diff --git a/src/main/java/org/traccar/protocol/Gl200ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200ProtocolDecoder.java index ca1df7a13..9b4e05c35 100644 --- a/src/main/java/org/traccar/protocol/Gl200ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gl200ProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 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. @@ -15,12 +15,14 @@ */ package org.traccar.protocol; +import com.google.inject.Injector; import org.traccar.BaseProtocolDecoder; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import org.traccar.Protocol; +import jakarta.inject.Inject; import java.net.SocketAddress; public class Gl200ProtocolDecoder extends BaseProtocolDecoder { @@ -34,6 +36,12 @@ public class Gl200ProtocolDecoder extends BaseProtocolDecoder { binaryProtocolDecoder = new Gl200BinaryProtocolDecoder(protocol); } + @Inject + public void setInjector(Injector injector) { + injector.injectMembers(textProtocolDecoder); + injector.injectMembers(binaryProtocolDecoder); + } + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java index 280986165..911af8d73 100644 --- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 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. @@ -15,9 +15,10 @@ */ package org.traccar.protocol; +import io.netty.buffer.Unpooled; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; +import org.traccar.helper.DataConverter; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.config.Keys; @@ -46,11 +47,15 @@ import java.util.regex.Pattern; public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { - private final boolean ignoreFixTime; + private boolean ignoreFixTime; public Gl200TextProtocolDecoder(Protocol protocol) { super(protocol); - ignoreFixTime = Context.getConfig().getBoolean(Keys.PROTOCOL_IGNORE_FIX_TIME.withPrefix(getProtocolName())); + } + + @Override + protected void init() { + ignoreFixTime = getConfig().getBoolean(Keys.PROTOCOL_IGNORE_FIX_TIME.withPrefix(getProtocolName())); } private static final Pattern PATTERN_ACK = new PatternBuilder() @@ -65,340 +70,6 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { .text("$").optional() .compile(); - private static final Pattern PATTERN_INF = new PatternBuilder() - .text("+").expression("(?:RESP|BUFF):GTINF,") - .number("[0-9A-Z]{2}xxxx,") // 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 - .number("(d{1,2}),") // rssi - .number("d{1,2},") - .expression("[01]{1,2},") // external power - .number("([d.]+)?,") // odometer or external power - .number("d*,") // backup battery or lightness - .number("(d+.d+),") // battery - .expression("([01]),") // charging - .number("(?:d),") // led - .number("(?:d)?,") // gps on need - .number("(?:d)?,") // gps antenna type - .number("(?:d)?,").optional() // gps antenna state - .number("d{14},") // last fix time - .groupBegin() - .number("(d+),") // battery percentage - .number("[d.]*,") // flash type / power - .number("(-?[d.]+)?,,,") // temperature - .or() - .expression("(?:[01])?,").optional() // pin15 mode - .number("(d+)?,") // adc1 - .number("(d+)?,").optional() // adc2 - .number("(xx)?,") // digital input - .number("(xx)?,") // digital output - .number("[-+]dddd,") // timezone - .expression("[01],") // daylight saving - .or() - .any() - .groupEnd() - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd),") // time (hhmmss) - .number("(xxxx)") // counter - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_VER = new PatternBuilder() - .text("+").expression("(?:RESP|BUFF):GTVER,") - .number("[0-9A-Z]{2}xxxx,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("[^,]*,") // device name - .expression("([^,]*),") // device type - .number("(xxxx),") // firmware version - .number("(xxxx),") // hardware version - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd),") // time (hhmmss) - .number("(xxxx)") // counter - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_LOCATION = new PatternBuilder() - .number("(d{1,2}.?d?)?,") // hdop - .number("(d{1,3}.d)?,") // speed - .number("(d{1,3}.?d?)?,") // course - .number("(-?d{1,5}.d)?,") // altitude - .number("(-?d{1,3}.d{6})?,") // longitude - .number("(-?d{1,2}.d{6})?,") // latitude - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(d+)?,") // mcc - .number("(d+)?,") // mnc - .groupBegin() - .number("(d+),") // lac - .number("(d+),") // cid - .or() - .number("(x+)?,") // lac - .number("(x+)?,") // cid - .groupEnd() - .number("(?:d+|(d+.d))?,") // rssi / odometer - .compile(); - - private static final Pattern PATTERN_OBD = new PatternBuilder() - .text("+RESP:GTOBD,") - .number("[0-9A-Z]{2}xxxx,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("(?:[0-9A-Z]{17})?,") // vin - .expression("[^,]{0,20},") // device name - .expression("[01],") // report type - .number("x{1,8},") // report mask - .expression("(?:[0-9A-Z]{17})?,") // vin - .number("[01],") // obd connect - .number("(?:d{1,5})?,") // obd voltage - .number("(?:x{8})?,") // support pids - .number("(d{1,5})?,") // engine rpm - .number("(d{1,3})?,") // speed - .number("(-?d{1,3})?,") // coolant temp - .number("(d+.?d*|Inf|NaN)?,") // fuel consumption - .number("(d{1,5})?,") // dtcs cleared distance - .number("(?:d{1,5})?,") - .expression("([01])?,") // obd connect - .number("(d{1,3})?,") // number of dtcs - .number("(x*),") // dtcs - .number("(d{1,3})?,") // throttle - .number("(?:d{1,3})?,") // engine load - .number("(d{1,3})?,") // fuel level - .expression("(?:[0-9A],)?") // obd protocol - .number("(d+),") // odometer - .expression(PATTERN_LOCATION.pattern()) - .number("(d{1,7}.d)?,") // odometer - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_FRI = new PatternBuilder() - .text("+").expression("(?:RESP|BUFF):GT...,") - .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("(?:([0-9A-Z]{17}),)?") // vin - .expression("[^,]*,") // device name - .number("(d+)?,") // power - .number("(d{1,2}),").optional() // report type - .number("d{1,2},").optional() // count - .number("d*,").optional() // reserved - .number("(d+),").optional() // battery - .expression("((?:") - .expression(PATTERN_LOCATION.pattern()) - .expression(")+)") - .groupBegin() - .number("d{1,2},,") - .number("(d{1,3}),") // battery - .number("[01],") // mode - .number("(?:[01])?,") // motion - .number("(?:-?d{1,2}.d)?,") // temperature - .or() - .number("(d{1,7}.d)?,") // odometer - .number("(d{5}:dd:dd)?,") // hour meter - .number("(x+)?,") // adc 1 - .number("(x+)?,") // adc 2 - .number("(d{1,3})?,") // battery - .number("(?:(xx)(xx)(xx))?,") // device status - .number("(d+)?,") // rpm - .number("(?:d+.?d*|Inf|NaN)?,") // fuel consumption - .number("(d+)?,") // fuel level - .or() - .number("(-?d),") // rssi - .number("(d{1,3}),") // battery - .or() - .number("(d{1,7}.d)?,").optional() // odometer - .number("(d{1,3})?,") // battery - .groupEnd() - .any() - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_ERI = new PatternBuilder() - .text("+").expression("(?:RESP|BUFF):GTERI,") - .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("[^,]*,") // device name - .number("(x{8}),") // mask - .number("(d+)?,") // power - .number("d{1,2},") // report type - .number("d{1,2},") // count - .expression("((?:") - .expression(PATTERN_LOCATION.pattern()) - .expression(")+)") - .groupBegin() - .number("(d{1,7}.d)?,") // odometer - .number("(d{5}:dd:dd)?,") // hour meter - .number("(x+)?,") // adc 1 - .number("(x+)?,").optional() // adc 2 - .groupBegin() - .number("(x+)?,") // adc 3 - .number("(xx),") // inputs - .number("(xx),") // outputs - .or() - .number("(d{1,3})?,") // battery - .number("(?:(xx)(xx)(xx))?,") // device status - .groupEnd() - .expression("(.*)") // additional data - .or() - .number("d*,,") - .number("(d+),") // battery - .any() - .groupEnd() - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_IGN = new PatternBuilder() - .text("+").expression("(?:RESP|BUFF):GTIG[NF],") - .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("[^,]*,") // device name - .number("d+,") // ignition off duration - .expression(PATTERN_LOCATION.pattern()) - .number("(d{5}:dd:dd)?,") // hour meter - .number("(d{1,7}.d)?,") // odometer - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_LSW = new PatternBuilder() - .text("+RESP:").expression("GT[LT]SW,") - .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("[^,]*,") // device name - .number("[01],") // type - .number("([01]),") // state - .expression(PATTERN_LOCATION.pattern()) - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_IDA = new PatternBuilder() - .text("+RESP:GTIDA,") - .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("[^,]*,,") // device name - .number("([^,]+),") // rfid - .expression("[01],") // report type - .number("1,") // count - .expression(PATTERN_LOCATION.pattern()) - .number("(d+.d),") // odometer - .text(",,,,") - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_WIF = new PatternBuilder() - .text("+RESP:GTWIF,") - .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("[^,]*,") // device name - .number("(d+),") // count - .number("((?:x{12},-?d+,,,,)+),,,,") // wifi - .number("(d{1,3}),") // battery - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_GSM = new PatternBuilder() - .text("+RESP:GTGSM,") - .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("(?:STR|CTN|NMR|RTL),") // fix type - .expression("(.*)") // cells - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_PNA = new PatternBuilder() - .text("+RESP:GT").expression("P[NF]A,") - .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("[^,]*,") // device name - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN = new PatternBuilder() - .text("+").expression("(?:RESP|BUFF):GT...,") - .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("[^,]*,") // device name - .number("d*,") - .number("(x{1,2}),") // report type - .number("d{1,2},") // count - .expression(PATTERN_LOCATION.pattern()) - .groupBegin() - .number("(d{1,7}.d)?,").optional() // odometer - .number("(d{1,3})?,") // battery - .or() - .number("(d{1,7}.d)?,") // odometer - .groupEnd() - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)") // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_BASIC = new PatternBuilder() - .text("+").expression("(?:RESP|BUFF)").text(":") - .expression("GT...,") - .number("(?:[0-9A-Z]{2}xxxx)?,").optional() // protocol version - .number("(d{15}|x{14}),") // imei - .any() - .text(",") - .number("(d{1,2})?,") // hdop - .number("(d{1,3}.d)?,") // speed - .number("(d{1,3})?,") // course - .number("(-?d{1,5}.d)?,") // altitude - .number("(-?d{1,3}.d{6})?,") // longitude - .number("(-?d{1,2}.d{6})?,") // latitude - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(d+),") // mcc - .number("(d+),") // mnc - .number("(x+),") // lac - .number("(x+),").optional(4) // cell - .any() - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - private Object decodeAck(Channel channel, SocketAddress remoteAddress, String sentence, String type) { Parser parser = new Parser(PATTERN_ACK, sentence); if (parser.matches()) { @@ -450,13 +121,54 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { private Long parseHours(String hoursString) { if (hoursString != null) { String[] hours = hoursString.split(":"); - return (long) (Integer.parseInt(hours[0]) * 3600 - + (hours.length > 1 ? Integer.parseInt(hours[1]) * 60 : 0) + return (Integer.parseInt(hours[0]) * 3600L + + (hours.length > 1 ? Integer.parseInt(hours[1]) * 60L : 0) + (hours.length > 2 ? Integer.parseInt(hours[2]) : 0)) * 1000; } return null; } + private static final Pattern PATTERN_INF = new PatternBuilder() + .text("+").expression("(?:RESP|BUFF):GTINF,") + .number("[0-9A-Z]{2}xxxx,") // 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 + .number("(d{1,2}),") // rssi + .number("d{1,2},") + .expression("[01]{1,2},") // external power + .number("([d.]+)?,") // odometer or external power + .number("d*,") // backup battery or lightness + .number("(d+.d+),") // battery + .expression("([01]),") // charging + .number("(?:d),") // led + .number("(?:d)?,") // gps on need + .number("(?:d)?,") // gps antenna type + .number("(?:d)?,").optional() // gps antenna state + .number("d{14},") // last fix time + .groupBegin() + .number("(d+),") // battery percentage + .number("[d.]*,") // flash type / power + .number("(-?[d.]+)?,,,") // temperature + .or() + .expression("(?:[01])?,").optional() // pin15 mode + .number("(d+)?,") // adc1 + .number("(d+)?,").optional() // adc2 + .number("(xx)?,") // digital input + .number("(xx)?,") // digital output + .number("[-+]dddd,") // timezone + .expression("[01],") // daylight saving + .or() + .any() + .groupEnd() + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd),") // time (hhmmss) + .number("(xxxx)") // counter + .text("$").optional() + .compile(); + private Object decodeInf(Channel channel, SocketAddress remoteAddress, String sentence) { Parser parser = new Parser(PATTERN_INF, sentence); Position position = initPosition(parser, channel, remoteAddress); @@ -517,6 +229,20 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { return position; } + private static final Pattern PATTERN_VER = new PatternBuilder() + .text("+").expression("(?:RESP|BUFF):GTVER,") + .number("[0-9A-Z]{2}xxxx,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,") // device name + .expression("([^,]*),") // device type + .number("(xxxx),") // firmware version + .number("(xxxx),") // hardware version + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd),") // time (hhmmss) + .number("(xxxx)") // counter + .text("$").optional() + .compile(); + private Object decodeVer(Channel channel, SocketAddress remoteAddress, String sentence) { Parser parser = new Parser(PATTERN_VER, sentence); Position position = initPosition(parser, channel, remoteAddress); @@ -537,6 +263,28 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { parser.skip(19); } + private static final Pattern PATTERN_LOCATION = new PatternBuilder() + .number("(d{1,2}.?d?)?,") // hdop + .number("(d{1,3}.d)?,") // speed + .number("(d{1,3}.?d?)?,") // course + .number("(-?d{1,5}.d)?,") // altitude + .number("(-?d{1,3}.d{6})?,") // longitude + .number("(-?d{1,2}.d{6})?,") // latitude + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(d+)?,") // mcc + .number("(d+)?,") // mnc + .groupBegin() + .number("(d+),") // lac + .number("(d+),") // cid + .or() + .number("(x+)?,") // lac + .number("(x+)?,") // cid + .groupEnd() + .number("(?:d+|(d+.d))?,") // rssi / odometer + .compile(); + private void decodeLocation(Position position, Parser parser) { Double hdop = parser.nextDouble(); position.setValid(hdop == null || hdop > 0); @@ -571,6 +319,41 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { } } + private static final Pattern PATTERN_OBD = new PatternBuilder() + .text("+RESP:GTOBD,") + .number("[0-9A-Z]{2}xxxx,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("(?:[0-9A-Z]{17})?,") // vin + .expression("[^,]{0,20},") // device name + .expression("[01],") // report type + .number("x{1,8},") // report mask + .expression("(?:[0-9A-Z]{17})?,") // vin + .number("[01],") // obd connect + .number("(?:d{1,5})?,") // obd voltage + .number("(?:x{8})?,") // support pids + .number("(d{1,5})?,") // engine rpm + .number("(d{1,3})?,") // speed + .number("(-?d{1,3})?,") // coolant temp + .number("(d+.?d*|Inf|NaN)?,") // fuel consumption + .number("(d{1,5})?,") // dtcs cleared distance + .number("(?:d{1,5})?,") + .expression("([01])?,") // obd connect + .number("(d{1,3})?,") // number of dtcs + .number("(x*),") // dtcs + .number("(d{1,3})?,") // throttle + .number("(?:d{1,3})?,") // engine load + .number("(d{1,3})?,") // fuel level + .expression("(?:[0-9A],)?") // obd protocol + .number("(d+),") // odometer + .expression(PATTERN_LOCATION.pattern()) + .number("(d{1,7}.d)?,") // odometer + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + private Object decodeObd(Channel channel, SocketAddress remoteAddress, String sentence) { Parser parser = new Parser(PATTERN_OBD, sentence); Position position = initPosition(parser, channel, remoteAddress); @@ -617,7 +400,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]); position.setDeviceId(deviceSession.getDeviceId()); - index += 1; // device name + String deviceName = values[index++]; index += 1; // report type index += 1; // canbus state long reportMask = Long.parseLong(values[index++], 16); @@ -659,11 +442,11 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { if (BitUtil.check(reportMask, 11) && !values[index++].isEmpty()) { position.set(Position.KEY_HOURS, UnitsConverter.msFromHours(Double.parseDouble(values[index - 1]))); } - if (BitUtil.check(reportMask, 12)) { - position.set("drivingHours", Double.parseDouble(values[index++])); + if (BitUtil.check(reportMask, 12) && !values[index++].isEmpty()) { + position.set(Position.KEY_DRIVING_TIME, Double.parseDouble(values[index - 1])); } - if (BitUtil.check(reportMask, 13)) { - position.set("idleHours", Double.parseDouble(values[index++])); + if (BitUtil.check(reportMask, 13) && !values[index++].isEmpty()) { + position.set("idleHours", Double.parseDouble(values[index - 1])); } if (BitUtil.check(reportMask, 14) && !values[index++].isEmpty()) { position.set("idleFuelConsumption", Double.parseDouble(values[index - 1])); @@ -689,8 +472,19 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { if (BitUtil.check(reportMask, 21) && !values[index++].isEmpty()) { position.set("engineOverspeed", Double.parseDouble(values[index - 1])); } - if (BitUtil.check(reportMask, 29)) { - reportMaskExt = Long.parseLong(values[index++], 16); + if ("GV350M".equals(deviceName)) { + if (BitUtil.check(reportMask, 22)) { + index += 1; // impulse distance + } + if (BitUtil.check(reportMask, 23)) { + index += 1; // gross vehicle weight + } + if (BitUtil.check(reportMask, 24)) { + index += 1; // catalyst liquid level + } + } + if (BitUtil.check(reportMask, 29) && !values[index++].isEmpty()) { + reportMaskExt = Long.parseLong(values[index - 1], 16); } if (BitUtil.check(reportMaskExt, 0) && !values[index++].isEmpty()) { position.set("adBlueLevel", Integer.parseInt(values[index - 1])); @@ -821,6 +615,51 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { } } + private static final Pattern PATTERN_FRI = new PatternBuilder() + .text("+").expression("(?:RESP|BUFF):GT...,") + .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("(?:([0-9A-Z]{17}),)?") // vin + .expression("[^,]*,") // device name + .number("(d+)?,") // power + .number("(d{1,2}),").optional() // report type + .number("d{1,2},").optional() // count + .number("d*,").optional() // reserved + .number("(d+),").optional() // battery + .expression("((?:") + .expression(PATTERN_LOCATION.pattern()) + .expression(")+)") + .groupBegin() + .number("d{1,2},,") + .number("(d{1,3}),") // battery + .number("[01],") // mode + .number("(?:[01])?,") // motion + .number("(?:-?d{1,2}.d)?,") // temperature + .or() + .number("(d{1,7}.d)?,") // odometer + .number("(d{5}:dd:dd)?,") // hour meter + .number("(x+)?,") // adc 1 + .number("(x+)?,") // adc 2 + .number("(d{1,3})?,") // battery + .number("(?:(xx)(xx)(xx))?,") // device status + .number("(d+)?,") // rpm + .number("(?:d+.?d*|Inf|NaN)?,") // fuel consumption + .number("(d+)?,") // fuel level + .or() + .number("(-?d),") // rssi + .number("(d{1,3}),") // battery + .or() + .number("(d{1,7}.d)?,").optional() // odometer + .number("(d{1,3})?,") // battery + .groupEnd() + .any() + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + private Object decodeFri(Channel channel, SocketAddress remoteAddress, String sentence) { Parser parser = new Parser(PATTERN_FRI, sentence); if (!parser.matches()) { @@ -901,6 +740,44 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { return positions; } + private static final Pattern PATTERN_ERI = new PatternBuilder() + .text("+").expression("(?:RESP|BUFF):GTERI,") + .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,") // device name + .number("(x{8}),") // mask + .number("(d+)?,") // power + .number("d{1,2},") // report type + .number("d{1,2},") // count + .expression("((?:") + .expression(PATTERN_LOCATION.pattern()) + .expression(")+)") + .groupBegin() + .number("(d{1,7}.d)?,") // odometer + .number("(d{5}:dd:dd)?,") // hour meter + .number("(x+)?,") // adc 1 + .number("(x+)?,").optional() // adc 2 + .groupBegin() + .number("(x+)?,") // adc 3 + .number("(xx),") // inputs + .number("(xx),") // outputs + .or() + .number("(d{1,3})?,") // battery + .number("(?:(xx)(xx)(xx))?,") // device status + .groupEnd() + .expression("(.*)") // additional data + .or() + .number("d*,,") + .number("(d+),") // battery + .any() + .groupEnd() + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + private Object decodeEri(Channel channel, SocketAddress remoteAddress, String sentence) { Parser parser = new Parser(PATTERN_ERI, sentence); if (!parser.matches()) { @@ -936,7 +813,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_POWER, power * 0.001); } - if (parser.hasNext(12)) { + if (parser.hasNextAny(12)) { position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000); position.set(Position.KEY_HOURS, parseHours(parser.next())); @@ -958,7 +835,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { index += 1; // device type if (BitUtil.check(mask, 0)) { - index += 1; // digital fuel sensor data + position.set(Position.KEY_FUEL_LEVEL, Integer.parseInt(data[index++], 16)); } if (BitUtil.check(mask, 1)) { @@ -1004,6 +881,22 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { return positions; } + private static final Pattern PATTERN_IGN = new PatternBuilder() + .text("+").expression("(?:RESP|BUFF):GTIG[NF],") + .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,") // device name + .number("d+,") // ignition off duration + .expression(PATTERN_LOCATION.pattern()) + .number("(d{5}:dd:dd)?,") // hour meter + .number("(d{1,7}.d)?,") // odometer + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + private Object decodeIgn(Channel channel, SocketAddress remoteAddress, String sentence) { Parser parser = new Parser(PATTERN_IGN, sentence); Position position = initPosition(parser, channel, remoteAddress); @@ -1022,6 +915,21 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { return position; } + private static final Pattern PATTERN_LSW = new PatternBuilder() + .text("+RESP:").expression("GT[LT]SW,") + .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,") // device name + .number("[01],") // type + .number("([01]),") // state + .expression(PATTERN_LOCATION.pattern()) + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + private Object decodeLsw(Channel channel, SocketAddress remoteAddress, String sentence) { Parser parser = new Parser(PATTERN_LSW, sentence); Position position = initPosition(parser, channel, remoteAddress); @@ -1038,6 +946,24 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { return position; } + private static final Pattern PATTERN_IDA = new PatternBuilder() + .text("+RESP:GTIDA,") + .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,,") // device name + .number("([^,]+),") // rfid + .expression("[01],") // report type + .number("1,") // count + .expression(PATTERN_LOCATION.pattern()) + .number("(d+.d),") // odometer + .text(",,,,") + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + private Object decodeIda(Channel channel, SocketAddress remoteAddress, String sentence) { Parser parser = new Parser(PATTERN_IDA, sentence); Position position = initPosition(parser, channel, remoteAddress); @@ -1056,6 +982,21 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { return position; } + private static final Pattern PATTERN_WIF = new PatternBuilder() + .text("+RESP:GTWIF,") + .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,") // device name + .number("(d+),") // count + .number("((?:x{12},-?d+,,,,)+),,,,") // wifi + .number("(d{1,3}),") // battery + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + private Object decodeWif(Channel channel, SocketAddress remoteAddress, String sentence) { Parser parser = new Parser(PATTERN_WIF, sentence); Position position = initPosition(parser, channel, remoteAddress); @@ -1082,6 +1023,19 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { return position; } + private static final Pattern PATTERN_GSM = new PatternBuilder() + .text("+RESP:GTGSM,") + .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("(?:STR|CTN|NMR|RTL),") // fix type + .expression("(.*)") // cells + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + private Object decodeGsm(Channel channel, SocketAddress remoteAddress, String sentence) { Parser parser = new Parser(PATTERN_GSM, sentence); Position position = initPosition(parser, channel, remoteAddress); @@ -1108,6 +1062,18 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { return position; } + private static final Pattern PATTERN_PNA = new PatternBuilder() + .text("+RESP:GT").expression("P[NF]A,") + .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,") // device name + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + private Object decodePna(Channel channel, SocketAddress remoteAddress, String sentence) { Parser parser = new Parser(PATTERN_PNA, sentence); Position position = initPosition(parser, channel, remoteAddress); @@ -1122,6 +1088,204 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { return position; } + private static final Pattern PATTERN_DAR = new PatternBuilder() + .text("+RESP:GTDAR,") + .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,") // device name + .number("(d),") // warning type + .number("(d{1,2}),,,") // fatigue degree + .expression(PATTERN_LOCATION.pattern()) + .any() + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + + private Object decodeDar(Channel channel, SocketAddress remoteAddress, String sentence) { + Parser parser = new Parser(PATTERN_DAR, sentence); + Position position = initPosition(parser, channel, remoteAddress); + if (position == null) { + return null; + } + + int warningType = parser.nextInt(); + int fatigueDegree = parser.nextInt(); + if (warningType == 1) { + position.set(Position.KEY_ALARM, Position.ALARM_FATIGUE_DRIVING); + position.set("fatigueDegree", fatigueDegree); + } else { + position.set("warningType", warningType); + } + + decodeLocation(position, parser); + + decodeDeviceTime(position, parser); + + return position; + } + + private static final Pattern PATTERN_DTT = new PatternBuilder() + .text("+RESP:GTDTT,") + .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,,,") // device name + .number("d,") // data type + .number("d+,") // data length + .number("(x+),") // data + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + + private Object decodeDtt(Channel channel, SocketAddress remoteAddress, String sentence) { + Parser parser = new Parser(PATTERN_DTT, sentence); + Position position = initPosition(parser, channel, remoteAddress); + if (position == null) { + return null; + } + + getLastLocation(position, null); + + String data = Unpooled.wrappedBuffer(DataConverter.parseHex(parser.next())) + .toString(StandardCharsets.US_ASCII); + if (data.contains("COMB")) { + String[] values = data.split(","); + position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(values[2])); + } else { + position.set(Position.KEY_RESULT, data); + } + + decodeDeviceTime(position, parser); + + return position; + } + + private static final Pattern PATTERN_BAA = new PatternBuilder() + .text("+RESP:GTBAA,") + .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,") // device name + .number("x+,") // index + .number("d,") // accessory type + .number("d,") // accessory model + .number("x+,") // alarm type + .number("(x{4}),") // append mask + .expression("((?:[^,]+,){0,6})") // accessory optionals + .expression(PATTERN_LOCATION.pattern()) + .any() + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + + private Object decodeBaa(Channel channel, SocketAddress remoteAddress, String sentence) { + Parser parser = new Parser(PATTERN_BAA, sentence); + Position position = initPosition(parser, channel, remoteAddress); + if (position == null) { + return null; + } + + int mask = parser.nextHexInt(); + String[] values = parser.next().split(","); + int index = 0; + if (BitUtil.check(mask, 0)) { + position.set("accessoryName", values[index++]); + } + if (BitUtil.check(mask, 1)) { + position.set("accessoryMac", values[index++]); + } + if (BitUtil.check(mask, 2)) { + position.set("accessoryStatus", Integer.parseInt(values[index++])); + } + if (BitUtil.check(mask, 3)) { + position.set("accessoryVoltage", Integer.parseInt(values[index++]) * 0.001); + } + if (BitUtil.check(mask, 4)) { + position.set("accessoryTemp", Integer.parseInt(values[index++])); + } + if (BitUtil.check(mask, 5)) { + position.set("accessoryHumidity", Integer.parseInt(values[index])); + } + + decodeLocation(position, parser); + + decodeDeviceTime(position, parser); + + return position; + } + + private static final Pattern PATTERN_BID = new PatternBuilder() + .text("+RESP:GTBID,") + .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,") // device name + .number("d,") // count + .number("d,") // accessory model + .number("(x{4}),") // append mask + .expression("((?:[^,]+,){0,2})") // accessory optionals + .expression(PATTERN_LOCATION.pattern()) + .any() + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + + private Object decodeBid(Channel channel, SocketAddress remoteAddress, String sentence) { + Parser parser = new Parser(PATTERN_BID, sentence); + Position position = initPosition(parser, channel, remoteAddress); + if (position == null) { + return null; + } + + int mask = parser.nextHexInt(); + String[] values = parser.next().split(","); + int index = 0; + if (BitUtil.check(mask, 1)) { + position.set("accessoryMac", values[index++]); + } + if (BitUtil.check(mask, 3)) { + position.set("accessoryVoltage", Integer.parseInt(values[index]) * 0.001); + } + + decodeLocation(position, parser); + + decodeDeviceTime(position, parser); + + return position; + } + + private static final Pattern PATTERN = new PatternBuilder() + .text("+").expression("(?:RESP|BUFF):GT...,") + .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,") // device name + .number("d*,") + .number("(x{1,2}),") // report type + .number("d{1,2},") // count + .number("d*,").optional() // reserved + .expression(PATTERN_LOCATION.pattern()) + .groupBegin() + .number("(?:(d{1,7}.d)|0)?,").optional() // odometer + .number("(d{1,3})?,") // battery + .or() + .number("(d{1,7}.d)?,") // odometer + .groupEnd() + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)") // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + private Object decodeOther(Channel channel, SocketAddress remoteAddress, String sentence, String type) { Parser parser = new Parser(PATTERN, sentence); Position position = initPosition(parser, channel, remoteAddress); @@ -1172,6 +1336,34 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { return position; } + private static final Pattern PATTERN_BASIC = new PatternBuilder() + .text("+").expression("(?:RESP|BUFF)").text(":") + .expression("GT...,") + .number("(?:[0-9A-Z]{2}xxxx)?,").optional() // protocol version + .number("(d{15}|x{14}),") // imei + .any() + .text(",") + .number("(d{1,2})?,") // hdop + .number("(d{1,3}.d)?,") // speed + .number("(d{1,3})?,") // course + .number("(-?d{1,5}.d)?,") // altitude + .number("(-?d{1,3}.d{6})?,") // longitude + .number("(-?d{1,2}.d{6})?,") // latitude + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(d+),") // mcc + .number("(d+),") // mnc + .number("(x+),") // lac + .number("(x+),").optional(4) // cell + .any() + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + private Object decodeBasic(Channel channel, SocketAddress remoteAddress, String sentence, String type) { Parser parser = new Parser(PATTERN_BASIC, sentence); Position position = initPosition(parser, channel, remoteAddress); @@ -1313,6 +1505,18 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { case "PFA": result = decodePna(channel, remoteAddress, sentence); break; + case "DAR": + result = decodeDar(channel, remoteAddress, sentence); + break; + case "DTT": + result = decodeDtt(channel, remoteAddress, sentence); + break; + case "BAA": + result = decodeBaa(channel, remoteAddress, sentence); + break; + case "BID": + result = decodeBid(channel, remoteAddress, sentence); + break; default: result = decodeOther(channel, remoteAddress, sentence, type); break; @@ -1333,7 +1537,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { } } - if (channel != null && Context.getConfig().getBoolean(Keys.PROTOCOL_ACK.withPrefix(getProtocolName()))) { + if (channel != null && getConfig().getBoolean(Keys.PROTOCOL_ACK.withPrefix(getProtocolName()))) { String checksum; if (sentence.endsWith("$")) { checksum = sentence.substring(sentence.length() - 1 - 4, sentence.length() - 1); diff --git a/src/main/java/org/traccar/protocol/GlobalSatProtocol.java b/src/main/java/org/traccar/protocol/GlobalSatProtocol.java index e86b5dc30..13f4f2646 100644 --- a/src/main/java/org/traccar/protocol/GlobalSatProtocol.java +++ b/src/main/java/org/traccar/protocol/GlobalSatProtocol.java @@ -21,18 +21,22 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class GlobalSatProtocol extends BaseProtocol { - public GlobalSatProtocol() { + @Inject + public GlobalSatProtocol(Config config) { setSupportedDataCommands( Command.TYPE_CUSTOM, Command.TYPE_ALARM_DISMISS, Command.TYPE_OUTPUT_CONTROL); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "!\r\n", "!")); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/GlobalSatProtocolDecoder.java b/src/main/java/org/traccar/protocol/GlobalSatProtocolDecoder.java index b48df4047..720b61695 100644 --- a/src/main/java/org/traccar/protocol/GlobalSatProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/GlobalSatProtocolDecoder.java @@ -17,8 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; @@ -40,9 +39,12 @@ public class GlobalSatProtocolDecoder extends BaseProtocolDecoder { public GlobalSatProtocolDecoder(Protocol protocol) { super(protocol); + } - format0 = Context.getConfig().getString(getProtocolName() + ".format0", "TSPRXAB27GHKLMnaicz*U!"); - format1 = Context.getConfig().getString(getProtocolName() + ".format1", "SARY*U!"); + @Override + protected void init() { + format0 = getConfig().getString(getProtocolName() + ".format0", "TSPRXAB27GHKLMnaicz*U!"); + format1 = getConfig().getString(getProtocolName() + ".format1", "SARY*U!"); } public void setFormat0(String format) { diff --git a/src/main/java/org/traccar/protocol/GlobalstarProtocol.java b/src/main/java/org/traccar/protocol/GlobalstarProtocol.java index 84cd5565b..1d9b6b6dd 100644 --- a/src/main/java/org/traccar/protocol/GlobalstarProtocol.java +++ b/src/main/java/org/traccar/protocol/GlobalstarProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class GlobalstarProtocol extends BaseProtocol { - public GlobalstarProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public GlobalstarProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new HttpResponseEncoder()); pipeline.addLast(new HttpRequestDecoder()); pipeline.addLast(new HttpObjectAggregator(65535)); diff --git a/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java b/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java index b742d0cac..0ddb95c14 100644 --- a/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java @@ -27,7 +27,7 @@ import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; import org.traccar.BaseHttpProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; @@ -151,15 +151,11 @@ public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder { position.setCourse(BitUtil.from(flags, 5) * 45); - position.setLatitude(buf.readUnsignedMedium() * 90.0 / (1 << 23)); - if (position.getLatitude() > 90) { - position.setLatitude(position.getLatitude() - 180); - } + double latitude = buf.readUnsignedMedium() * 90.0 / (1 << 23); + position.setLatitude(latitude > 90 ? latitude - 180 : latitude); - position.setLongitude(buf.readUnsignedMedium() * 180.0 / (1 << 23)); - if (position.getLongitude() > 180) { - position.setLongitude(position.getLongitude() - 360); - } + double longitude = buf.readUnsignedMedium() * 180.0 / (1 << 23); + position.setLongitude(longitude > 180 ? longitude - 360 : longitude); int speed = buf.readUnsignedByte(); position.setSpeed(UnitsConverter.knotsFromKph(speed)); diff --git a/src/main/java/org/traccar/protocol/GnxProtocol.java b/src/main/java/org/traccar/protocol/GnxProtocol.java index 3576bf805..cfa496009 100644 --- a/src/main/java/org/traccar/protocol/GnxProtocol.java +++ b/src/main/java/org/traccar/protocol/GnxProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class GnxProtocol extends BaseProtocol { - public GnxProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public GnxProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "\n\r")); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/GnxProtocolDecoder.java b/src/main/java/org/traccar/protocol/GnxProtocolDecoder.java index c9c221a69..9c8b6879a 100644 --- a/src/main/java/org/traccar/protocol/GnxProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/GnxProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/GoSafeProtocol.java b/src/main/java/org/traccar/protocol/GoSafeProtocol.java index aaaffac97..c9c0456a0 100644 --- a/src/main/java/org/traccar/protocol/GoSafeProtocol.java +++ b/src/main/java/org/traccar/protocol/GoSafeProtocol.java @@ -21,22 +21,26 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class GoSafeProtocol extends BaseProtocol { - public GoSafeProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public GoSafeProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '#')); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new GoSafeProtocolDecoder(GoSafeProtocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new GoSafeProtocolDecoder(GoSafeProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/GoSafeProtocolDecoder.java b/src/main/java/org/traccar/protocol/GoSafeProtocolDecoder.java index a86249224..f17ea0e08 100644 --- a/src/main/java/org/traccar/protocol/GoSafeProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/GoSafeProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; @@ -93,14 +93,14 @@ public class GoSafeProtocolDecoder extends BaseProtocolDecoder { position.setSpeed(UnitsConverter.knotsFromKph(Integer.parseInt(values[index - 1]))); } position.setCourse(Integer.parseInt(values[index++])); - if (index < values.length) { - position.setAltitude(Integer.parseInt(values[index++])); + if (index < values.length && !values[index++].isEmpty()) { + position.setAltitude(Integer.parseInt(values[index - 1])); } - if (index < values.length) { - position.set(Position.KEY_HDOP, Double.parseDouble(values[index++])); + if (index < values.length && !values[index++].isEmpty()) { + position.set(Position.KEY_HDOP, Double.parseDouble(values[index - 1])); } - if (index < values.length) { - position.set(Position.KEY_VDOP, Double.parseDouble(values[index++])); + if (index < values.length && !values[index++].isEmpty()) { + position.set(Position.KEY_VDOP, Double.parseDouble(values[index - 1])); } break; case "GSM": diff --git a/src/main/java/org/traccar/protocol/GotopProtocol.java b/src/main/java/org/traccar/protocol/GotopProtocol.java index 07fe02248..21fbbae99 100644 --- a/src/main/java/org/traccar/protocol/GotopProtocol.java +++ b/src/main/java/org/traccar/protocol/GotopProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class GotopProtocol extends BaseProtocol { - public GotopProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public GotopProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '#')); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/GotopProtocolDecoder.java b/src/main/java/org/traccar/protocol/GotopProtocolDecoder.java index a867451aa..5c8d0bac2 100644 --- a/src/main/java/org/traccar/protocol/GotopProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/GotopProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; @@ -35,7 +35,7 @@ public class GotopProtocolDecoder extends BaseProtocolDecoder { private static final Pattern PATTERN = new PatternBuilder() .number("(d+),") // imei - .expression("[^,]+,") // type + .expression("([^,]+),") // type .expression("([AV]),") // validity .number("DATE:(dd)(dd)(dd),") // date (yyddmm) .number("TIME:(dd)(dd)(dd),") // time (hhmmss) @@ -56,14 +56,25 @@ public class GotopProtocolDecoder extends BaseProtocolDecoder { return null; } - Position position = new Position(getProtocolName()); - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); if (deviceSession == null) { return null; } + + Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); + String type = parser.next(); + if (type.equals("CMD-KEY")) { + position.set(Position.KEY_ALARM, Position.ALARM_SOS); + } else if (type.startsWith("ALM-B")) { + if (Character.getNumericValue(type.charAt(5)) % 2 > 0) { + position.set(Position.KEY_ALARM, Position.ALARM_GEOFENCE_ENTER); + } else { + position.set(Position.KEY_ALARM, Position.ALARM_GEOFENCE_EXIT); + } + } + position.setValid(parser.next().equals("A")); position.setTime(parser.nextDateTime()); diff --git a/src/main/java/org/traccar/protocol/Gps056Protocol.java b/src/main/java/org/traccar/protocol/Gps056Protocol.java index b6ab10a19..44fc392be 100644 --- a/src/main/java/org/traccar/protocol/Gps056Protocol.java +++ b/src/main/java/org/traccar/protocol/Gps056Protocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class Gps056Protocol extends BaseProtocol { - public Gps056Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public Gps056Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new Gps056FrameDecoder()); pipeline.addLast(new Gps056ProtocolDecoder(Gps056Protocol.this)); } diff --git a/src/main/java/org/traccar/protocol/Gps056ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gps056ProtocolDecoder.java index 0ba79bb51..eea64364e 100644 --- a/src/main/java/org/traccar/protocol/Gps056ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gps056ProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; diff --git a/src/main/java/org/traccar/protocol/Gps103Protocol.java b/src/main/java/org/traccar/protocol/Gps103Protocol.java index 5356387ce..8424abfe5 100644 --- a/src/main/java/org/traccar/protocol/Gps103Protocol.java +++ b/src/main/java/org/traccar/protocol/Gps103Protocol.java @@ -21,11 +21,15 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class Gps103Protocol extends BaseProtocol { - public Gps103Protocol() { + @Inject + public Gps103Protocol(Config config) { setSupportedDataCommands( Command.TYPE_CUSTOM, Command.TYPE_POSITION_SINGLE, @@ -36,9 +40,9 @@ public class Gps103Protocol extends BaseProtocol { Command.TYPE_ALARM_ARM, Command.TYPE_ALARM_DISARM, Command.TYPE_REQUEST_PHOTO); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(2048, false, "\r\n", "\n", ";", "*")); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); @@ -46,9 +50,9 @@ public class Gps103Protocol extends BaseProtocol { pipeline.addLast(new Gps103ProtocolDecoder(Gps103Protocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new Gps103ProtocolEncoder(Gps103Protocol.this)); diff --git a/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java index d74f19179..d1c35b478 100644 --- a/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java @@ -19,8 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.DataConverter; @@ -57,9 +56,12 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder { .groupEnd() .expression("([^,]+)?,") // rfid .groupBegin() - .text("L,,,") + .text("L,") + .groupBegin() + .text(",,") .number("(x+),,") // lac .number("(x+),,,") // cid + .groupEnd("?") .or() .text("F,") .groupBegin() @@ -203,7 +205,7 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder { position.set(Position.PREFIX_TEMP + 1, Double.parseDouble(alarm.substring(2))); } else if (alarm.startsWith("oil ")) { position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(alarm.substring(4))); - } else if (!position.getAttributes().containsKey(Position.KEY_ALARM) && !alarm.equals("tracker")) { + } else if (!position.hasAttribute(Position.KEY_ALARM) && !alarm.equals("tracker")) { position.set(Position.KEY_EVENT, alarm); } @@ -219,12 +221,11 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder { } if (parser.hasNext(2)) { + position.setNetwork(new Network(CellTower.fromLacCid( + getConfig(), parser.nextHexInt(0), parser.nextHexInt(0)))); + } - getLastLocation(position, null); - - position.setNetwork(new Network(CellTower.fromLacCid(parser.nextHexInt(0), parser.nextHexInt(0)))); - - } else { + if (parser.hasNextAny(20)) { String utcHours = parser.next(); String utcMinutes = parser.next(); @@ -262,6 +263,10 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder { position.set("fuel2", parser.nextDouble()); position.set(Position.PREFIX_TEMP + 1, parser.nextInt()); + } else { + + getLastLocation(position, null); + } return position; @@ -361,7 +366,7 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder { getLastLocation(position, null); try { - position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(imei, photo, "jpg")); + position.set(Position.KEY_IMAGE, writeMediaFile(imei, photo, "jpg")); } finally { photoPackets = 0; photo.release(); diff --git a/src/main/java/org/traccar/protocol/Gps103ProtocolEncoder.java b/src/main/java/org/traccar/protocol/Gps103ProtocolEncoder.java index e662e9b04..9a899eeeb 100644 --- a/src/main/java/org/traccar/protocol/Gps103ProtocolEncoder.java +++ b/src/main/java/org/traccar/protocol/Gps103ProtocolEncoder.java @@ -49,7 +49,7 @@ public class Gps103ProtocolEncoder extends StringProtocolEncoder implements Stri case Command.TYPE_CUSTOM: return formatCommand(command, "**,imei:%s,%s", Command.KEY_UNIQUE_ID, Command.KEY_DATA); case Command.TYPE_POSITION_STOP: - return formatCommand(command, "**,imei:%s,A", Command.KEY_UNIQUE_ID); + return formatCommand(command, "**,imei:%s,D", Command.KEY_UNIQUE_ID); case Command.TYPE_POSITION_SINGLE: return formatCommand(command, "**,imei:%s,B", Command.KEY_UNIQUE_ID); case Command.TYPE_POSITION_PERIODIC: diff --git a/src/main/java/org/traccar/protocol/GpsGateProtocol.java b/src/main/java/org/traccar/protocol/GpsGateProtocol.java index a131b6f48..db1e8554a 100644 --- a/src/main/java/org/traccar/protocol/GpsGateProtocol.java +++ b/src/main/java/org/traccar/protocol/GpsGateProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class GpsGateProtocol extends BaseProtocol { - public GpsGateProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public GpsGateProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "\0", "\n", "\r\n")); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/GpsGateProtocolDecoder.java b/src/main/java/org/traccar/protocol/GpsGateProtocolDecoder.java index c158d3212..82da58f1e 100644 --- a/src/main/java/org/traccar/protocol/GpsGateProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/GpsGateProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.Checksum; diff --git a/src/main/java/org/traccar/protocol/GpsMarkerProtocol.java b/src/main/java/org/traccar/protocol/GpsMarkerProtocol.java index ad23ece48..f50088b2b 100644 --- a/src/main/java/org/traccar/protocol/GpsMarkerProtocol.java +++ b/src/main/java/org/traccar/protocol/GpsMarkerProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class GpsMarkerProtocol extends BaseProtocol { - public GpsMarkerProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public GpsMarkerProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "\r")); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/GpsMarkerProtocolDecoder.java b/src/main/java/org/traccar/protocol/GpsMarkerProtocolDecoder.java index bbb2c31e2..0fef4b7da 100644 --- a/src/main/java/org/traccar/protocol/GpsMarkerProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/GpsMarkerProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/GpsmtaProtocol.java b/src/main/java/org/traccar/protocol/GpsmtaProtocol.java index ce6cc5929..e146a816d 100644 --- a/src/main/java/org/traccar/protocol/GpsmtaProtocol.java +++ b/src/main/java/org/traccar/protocol/GpsmtaProtocol.java @@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class GpsmtaProtocol extends BaseProtocol { - public GpsmtaProtocol() { - addServer(new TrackerServer(true, getName()) { + @Inject + public GpsmtaProtocol(Config config) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new GpsmtaProtocolDecoder(GpsmtaProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/GpsmtaProtocolDecoder.java b/src/main/java/org/traccar/protocol/GpsmtaProtocolDecoder.java index 31f9401b4..a9b85d255 100644 --- a/src/main/java/org/traccar/protocol/GpsmtaProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/GpsmtaProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/GranitProtocol.java b/src/main/java/org/traccar/protocol/GranitProtocol.java index 244c3977b..9ca0fe25e 100644 --- a/src/main/java/org/traccar/protocol/GranitProtocol.java +++ b/src/main/java/org/traccar/protocol/GranitProtocol.java @@ -19,11 +19,15 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class GranitProtocol extends BaseProtocol { - public GranitProtocol() { + @Inject + public GranitProtocol(Config config) { setSupportedDataCommands( Command.TYPE_IDENTIFICATION, Command.TYPE_REBOOT_DEVICE, @@ -32,9 +36,9 @@ public class GranitProtocol extends BaseProtocol { setSupportedTextCommands( Command.TYPE_REBOOT_DEVICE, Command.TYPE_POSITION_PERIODIC); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new GranitFrameDecoder()); pipeline.addLast(new GranitProtocolEncoder(GranitProtocol.this)); pipeline.addLast(new GranitProtocolDecoder(GranitProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/GranitProtocolDecoder.java b/src/main/java/org/traccar/protocol/GranitProtocolDecoder.java index 292e43a0e..dfc3c10f6 100644 --- a/src/main/java/org/traccar/protocol/GranitProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/GranitProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/Gs100Protocol.java b/src/main/java/org/traccar/protocol/Gs100Protocol.java index a701815d0..715d48fc4 100644 --- a/src/main/java/org/traccar/protocol/Gs100Protocol.java +++ b/src/main/java/org/traccar/protocol/Gs100Protocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class Gs100Protocol extends BaseProtocol { - public Gs100Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public Gs100Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new Gs100ProtocolDecoder(Gs100Protocol.this)); } }); diff --git a/src/main/java/org/traccar/protocol/Gs100ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gs100ProtocolDecoder.java index 2496aad48..352070107 100644 --- a/src/main/java/org/traccar/protocol/Gs100ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gs100ProtocolDecoder.java @@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BcdUtil; diff --git a/src/main/java/org/traccar/protocol/Gt02Protocol.java b/src/main/java/org/traccar/protocol/Gt02Protocol.java index f412ee720..f448feacc 100644 --- a/src/main/java/org/traccar/protocol/Gt02Protocol.java +++ b/src/main/java/org/traccar/protocol/Gt02Protocol.java @@ -19,13 +19,17 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class Gt02Protocol extends BaseProtocol { - public Gt02Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public Gt02Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(256, 2, 1, 2, 0)); pipeline.addLast(new Gt02ProtocolDecoder(Gt02Protocol.this)); } diff --git a/src/main/java/org/traccar/protocol/Gt02ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt02ProtocolDecoder.java index 78a3fd3ee..4ecb0b43b 100644 --- a/src/main/java/org/traccar/protocol/Gt02ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gt02ProtocolDecoder.java @@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/Gt06Protocol.java b/src/main/java/org/traccar/protocol/Gt06Protocol.java index 9ec8de098..945ec3831 100644 --- a/src/main/java/org/traccar/protocol/Gt06Protocol.java +++ b/src/main/java/org/traccar/protocol/Gt06Protocol.java @@ -18,18 +18,22 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class Gt06Protocol extends BaseProtocol { - public Gt06Protocol() { + @Inject + public Gt06Protocol(Config config) { setSupportedDataCommands( Command.TYPE_ENGINE_STOP, Command.TYPE_ENGINE_RESUME, Command.TYPE_CUSTOM); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new Gt06FrameDecoder()); pipeline.addLast(new Gt06ProtocolEncoder(Gt06Protocol.this)); pipeline.addLast(new Gt06ProtocolDecoder(Gt06Protocol.this)); diff --git a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java index feacc1ae8..161d04d8d 100644 --- a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java @@ -1,5 +1,5 @@ /* -Supp * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,8 +20,8 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; +import org.traccar.helper.BufferUtil; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BcdUtil; @@ -32,7 +32,6 @@ import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; import org.traccar.helper.UnitsConverter; import org.traccar.model.CellTower; -import org.traccar.model.Device; import org.traccar.model.Network; import org.traccar.model.Position; import org.traccar.model.WifiAccessPoint; @@ -75,15 +74,16 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_LBS_EXTEND = 0x18; public static final int MSG_LBS_STATUS = 0x19; public static final int MSG_GPS_PHONE = 0x1A; - public static final int MSG_GPS_LBS_EXTEND = 0x1E; - public static final int MSG_HEARTBEAT = 0x23; - public static final int MSG_ADDRESS_REQUEST = 0x2A; - public static final int MSG_ADDRESS_RESPONSE = 0x97; - public static final int MSG_GPS_LBS_5 = 0x31; - public static final int MSG_GPS_LBS_STATUS_4 = 0x32; - public static final int MSG_WIFI_5 = 0x33; - public static final int MSG_AZ735_GPS = 0x32; // only extended - public static final int MSG_AZ735_ALARM = 0x33; // only extended + public static final int MSG_GPS_LBS_EXTEND = 0x1E; // JI09 + public static final int MSG_HEARTBEAT = 0x23; // GK310 + public static final int MSG_ADDRESS_REQUEST = 0x2A; // GK310 + public static final int MSG_ADDRESS_RESPONSE = 0x97; // GK310 + public static final int MSG_GPS_LBS_5 = 0x31; // AZ735 & SL4X + public static final int MSG_GPS_LBS_STATUS_4 = 0x32; // AZ735 & SL4X + public static final int MSG_WIFI_5 = 0x33; // AZ735 & SL4X + public static final int MSG_LBS_3 = 0x34; // SL4X + public static final int MSG_AZ735_GPS = 0x32; // AZ735 (extended) + public static final int MSG_AZ735_ALARM = 0x33; // AZ735 (only extended) public static final int MSG_X1_GPS = 0x34; public static final int MSG_X1_PHOTO_INFO = 0x35; public static final int MSG_X1_PHOTO_DATA = 0x36; @@ -93,33 +93,67 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_COMMAND_0 = 0x80; public static final int MSG_COMMAND_1 = 0x81; public static final int MSG_COMMAND_2 = 0x82; - public static final int MSG_TIME_REQUEST = 0x8A; + public static final int MSG_TIME_REQUEST = 0x8A; // GK310 public static final int MSG_INFO = 0x94; public static final int MSG_SERIAL = 0x9B; public static final int MSG_STRING_INFO = 0x21; - public static final int MSG_GPS_2 = 0xA0; - public static final int MSG_LBS_2 = 0xA1; - public static final int MSG_WIFI_3 = 0xA2; - public static final int MSG_FENCE_SINGLE = 0xA3; - public static final int MSG_FENCE_MULTI = 0xA4; - public static final int MSG_LBS_ALARM = 0xA5; - public static final int MSG_LBS_ADDRESS = 0xA7; - public static final int MSG_OBD = 0x8C; - public static final int MSG_DTC = 0x65; - public static final int MSG_PID = 0x66; - public static final int MSG_BMS = 0x20; - public static final int MSG_MULTIMEDIA = 0x21; - public static final int MSG_BMS_2 = 0x40; - public static final int MSG_MULTIMEDIA_2 = 0x41; - public static final int MSG_ALARM = 0x95; + public static final int MSG_GPS_LBS_7 = 0xA0; // GK310 & JM-VL03 + public static final int MSG_LBS_2 = 0xA1; // GK310 + public static final int MSG_WIFI_3 = 0xA2; // GK310 + public static final int MSG_FENCE_SINGLE = 0xA3; // GK310 + public static final int MSG_FENCE_MULTI = 0xA4; // GK310 & JM-LL301 + public static final int MSG_LBS_ALARM = 0xA5; // GK310 & JM-LL301 + public static final int MSG_LBS_ADDRESS = 0xA7; // GK310 + public static final int MSG_OBD = 0x8C; // FM08ABC + public static final int MSG_DTC = 0x65; // FM08ABC + public static final int MSG_PID = 0x66; // FM08ABC + public static final int MSG_BMS = 0x40; // WD-209 + public static final int MSG_MULTIMEDIA = 0x41; // WD-209 + public static final int MSG_ALARM = 0x95; // JC100 private enum Variant { VXT01, + WANWAY_S20, + SR411_MINI, + GT06E_CARD, + BENWAY, + S5, + SPACE10X, STANDARD, + OBD6, + WETRUST, + JC400, + SL4X, } private Variant variant; + private static final Pattern PATTERN_FUEL = new PatternBuilder() + .text("!AIOIL,") + .number("d+,") // device address + .number("d+.d+,") // output value + .number("(d+.d+),") // temperature + .expression("[^,]+,") // version + .number("dd") // back wave + .number("d") // software status code + .number("d,") // hardware status code + .number("(d+.d+),") // measured value + .expression("[01],") // movement status + .number("d+,") // excited wave times + .number("xx") // checksum + .compile(); + + private static final Pattern PATTERN_LOCATION = new PatternBuilder() + .text("Current position!") + .number("Lat:([NS])(d+.d+),") // latitude + .number("Lon:([EW])(d+.d+),") // longitude + .text("Course:").number("(d+.d+),") // course + .text("Speed:").number("(d+.d+),") // speed + .text("DateTime:") + .number("(dddd)-(dd)-(dd) +") // date + .number("(dd):(dd):(dd)") // time + .compile(); + private static boolean isSupported(int type) { return hasGps(type) || hasLbs(type) || hasStatus(type); } @@ -139,7 +173,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { case MSG_GPS_LBS_STATUS_4: case MSG_GPS_PHONE: case MSG_GPS_LBS_EXTEND: - case MSG_GPS_2: + case MSG_GPS_LBS_7: case MSG_FENCE_SINGLE: case MSG_FENCE_MULTI: return true; @@ -161,7 +195,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { case MSG_GPS_LBS_STATUS_2: case MSG_GPS_LBS_STATUS_3: case MSG_GPS_LBS_STATUS_4: - case MSG_GPS_2: + case MSG_GPS_LBS_7: case MSG_FENCE_SINGLE: case MSG_FENCE_MULTI: case MSG_LBS_ALARM: @@ -180,6 +214,8 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { case MSG_GPS_LBS_STATUS_2: case MSG_GPS_LBS_STATUS_3: case MSG_GPS_LBS_STATUS_4: + case MSG_FENCE_MULTI: + case MSG_LBS_ALARM: return true; default: return false; @@ -236,12 +272,12 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { } public static boolean decodeGps(Position position, ByteBuf buf, boolean hasLength, TimeZone timezone) { - return decodeGps(position, buf, hasLength, true, true, timezone); + return decodeGps(position, buf, hasLength, true, true, false, timezone); } public static boolean decodeGps( Position position, ByteBuf buf, boolean hasLength, boolean hasSatellites, - boolean hasSpeed, TimeZone timezone) { + boolean hasSpeed, boolean longSpeed, TimeZone timezone) { DateBuilder dateBuilder = new DateBuilder(timezone) .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()) @@ -260,7 +296,8 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { double longitude = buf.readUnsignedInt() / 60.0 / 30000.0; if (hasSpeed) { - position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte())); + position.setSpeed(UnitsConverter.knotsFromKph( + longSpeed ? buf.readUnsignedShort() : buf.readUnsignedByte())); } int flags = buf.readUnsignedShort(); @@ -305,9 +342,26 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { } int mcc = buf.readUnsignedShort(); - int mnc = BitUtil.check(mcc, 15) || type == MSG_GPS_LBS_6 ? buf.readUnsignedShort() : buf.readUnsignedByte(); - int lac = buf.readUnsignedShort(); - long cid = type == MSG_GPS_LBS_6 ? buf.readUnsignedInt() : buf.readUnsignedMedium(); + int mnc; + if (BitUtil.check(mcc, 15) || type == MSG_GPS_LBS_6) { + mnc = buf.readUnsignedShort(); + } else { + mnc = buf.readUnsignedByte(); + } + int lac; + if (type == MSG_LBS_ALARM || type == MSG_GPS_LBS_7) { + lac = buf.readInt(); + } else { + lac = buf.readUnsignedShort(); + } + long cid; + if (type == MSG_LBS_ALARM || type == MSG_GPS_LBS_7) { + cid = buf.readLong(); + } else if (type == MSG_GPS_LBS_6) { + cid = buf.readUnsignedInt(); + } else { + cid = buf.readUnsignedMedium(); + } position.setNetwork(new Network(CellTower.from(BitUtil.to(mcc, 15), mnc, lac, cid))); @@ -318,7 +372,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { return true; } - private boolean decodeStatus(Position position, ByteBuf buf, boolean batteryLevel) { + private void decodeStatus(Position position, ByteBuf buf) { int status = buf.readUnsignedByte(); @@ -353,15 +407,6 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { default: break; } - - if (batteryLevel) { - position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte() * 100 / 6); - } else { - position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01); - } - position.set(Position.KEY_RSSI, buf.readUnsignedByte()); - - return true; } private String decodeAlarm(short value) { @@ -381,13 +426,20 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { return Position.ALARM_OVERSPEED; case 0x0E: case 0x0F: + case 0x19: return Position.ALARM_LOW_BATTERY; case 0x11: return Position.ALARM_POWER_OFF; + case 0x0C: case 0x13: + case 0x25: return Position.ALARM_TAMPERING; case 0x14: return Position.ALARM_DOOR; + case 0x18: + return Position.ALARM_REMOVING; + case 0x23: + return Position.ALARM_FALL_DOWN; case 0x29: return Position.ALARM_ACCELERATION; case 0x30: @@ -397,81 +449,27 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { return Position.ALARM_CORNERING; case 0x2C: return Position.ALARM_ACCIDENT; - case 0x23: - return Position.ALARM_FALL_DOWN; default: return null; } } - private static final Pattern PATTERN_FUEL = new PatternBuilder() - .text("!AIOIL,") - .number("d+,") // device address - .number("d+.d+,") // output value - .number("(d+.d+),") // temperature - .expression("[^,]+,") // version - .number("dd") // back wave - .number("d") // software status code - .number("d,") // hardware status code - .number("(d+.d+),") // measured value - .expression("[01],") // movement status - .number("d+,") // excited wave times - .number("xx") // checksum - .compile(); - - private Position decodeFuelData(Position position, String sentence) { - Parser parser = new Parser(PATTERN_FUEL, sentence); - if (!parser.matches()) { - return null; - } - - position.set(Position.PREFIX_TEMP + 1, parser.nextDouble(0)); - position.set(Position.KEY_FUEL_LEVEL, parser.nextDouble(0)); - - return position; - } - - private static final Pattern PATTERN_LOCATION = new PatternBuilder() - .text("Current position!") - .number("Lat:([NS])(d+.d+),") // latitude - .number("Lon:([EW])(d+.d+),") // longitude - .text("Course:").number("(d+.d+),") // course - .text("Speed:").number("(d+.d+),") // speed - .text("DateTime:") - .number("(dddd)-(dd)-(dd) +") // date - .number("(dd):(dd):(dd)") // time - .compile(); - - private Position decodeLocationString(Position position, String sentence) { - Parser parser = new Parser(PATTERN_LOCATION, sentence); - if (!parser.matches()) { - return null; - } - - position.setValid(true); - position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG)); - position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG)); - position.setCourse(parser.nextDouble()); - position.setSpeed(parser.nextDouble()); - position.setTime(parser.nextDateTime(Parser.DateTimeFormat.YMD_HMS)); - - return position; - } - private Object decodeBasic(Channel channel, SocketAddress remoteAddress, ByteBuf buf) { int length = buf.readUnsignedByte(); int dataLength = length - 5; int type = buf.readUnsignedByte(); + Position position = new Position(getProtocolName()); DeviceSession deviceSession = null; if (type != MSG_LOGIN) { deviceSession = getDeviceSession(channel, remoteAddress); if (deviceSession == null) { return null; } - if (deviceSession.getTimeZone() == null) { - deviceSession.setTimeZone(getTimeZone(deviceSession.getDeviceId())); + position.setDeviceId(deviceSession.getDeviceId()); + if (!deviceSession.contains(DeviceSession.KEY_TIMEZONE)) { + deviceSession.set(DeviceSession.KEY_TIMEZONE, getTimeZone(deviceSession.getDeviceId())); } } @@ -481,8 +479,8 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedShort(); // type deviceSession = getDeviceSession(channel, remoteAddress, imei); - if (deviceSession != null && deviceSession.getTimeZone() == null) { - deviceSession.setTimeZone(getTimeZone(deviceSession.getDeviceId())); + if (deviceSession != null && !deviceSession.contains(DeviceSession.KEY_TIMEZONE)) { + deviceSession.set(DeviceSession.KEY_TIMEZONE, getTimeZone(deviceSession.getDeviceId())); } if (dataLength > 10) { @@ -494,23 +492,21 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { offset = -offset; } if (deviceSession != null) { - TimeZone timeZone = deviceSession.getTimeZone(); + TimeZone timeZone = deviceSession.get(DeviceSession.KEY_TIMEZONE); if (timeZone.getRawOffset() == 0) { timeZone.setRawOffset(offset * 1000); - deviceSession.setTimeZone(timeZone); + deviceSession.set(DeviceSession.KEY_TIMEZONE, timeZone); } } - } if (deviceSession != null) { sendResponse(channel, false, type, buf.getShort(buf.writerIndex() - 6), null); } - } else if (type == MSG_HEARTBEAT) { + return null; - Position position = new Position(getProtocolName()); - position.setDeviceId(deviceSession.getDeviceId()); + } else if (type == MSG_HEARTBEAT) { getLastLocation(position, null); @@ -539,6 +535,8 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { content.writeBytes(response.getBytes(StandardCharsets.US_ASCII)); sendResponse(channel, true, MSG_ADDRESS_RESPONSE, 0, content); + return null; + } else if (type == MSG_TIME_REQUEST) { Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); @@ -551,44 +549,13 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { content.writeByte(calendar.get(Calendar.SECOND)); sendResponse(channel, false, MSG_TIME_REQUEST, 0, content); - } else if (type == MSG_X1_GPS || type == MSG_X1_PHOTO_INFO) { - - return decodeX1(channel, buf, deviceSession, type); - - } else if (type == MSG_WIFI || type == MSG_WIFI_2 || type == MSG_WIFI_4) { - - return decodeWifi(channel, buf, deviceSession, type); - - } else if (type == MSG_INFO) { - - Position position = new Position(getProtocolName()); - position.setDeviceId(deviceSession.getDeviceId()); - - getLastLocation(position, null); - - position.set(Position.KEY_POWER, buf.readShort() * 0.01); - - return position; - - } else { - - return decodeBasicOther(channel, buf, deviceSession, type, dataLength); - - } - - return null; - } - - private Object decodeX1(Channel channel, ByteBuf buf, DeviceSession deviceSession, int type) { - - if (type == MSG_X1_GPS) { + return null; - Position position = new Position(getProtocolName()); - position.setDeviceId(deviceSession.getDeviceId()); + } else if (type == MSG_X1_GPS && variant != Variant.SL4X) { buf.readUnsignedInt(); // data and alarm - decodeGps(position, buf, false, deviceSession.getTimeZone()); + decodeGps(position, buf, false, deviceSession.get(DeviceSession.KEY_TIMEZONE)); buf.readUnsignedShort(); // terminal info @@ -632,125 +599,135 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { photos.put(pictureId, photo); sendPhotoRequest(channel, pictureId); - } - - return null; - } + return null; - private Object decodeWifi(Channel channel, ByteBuf buf, DeviceSession deviceSession, int type) { + } else if (type == MSG_WIFI || type == MSG_WIFI_2 || type == MSG_WIFI_4) { - Position position = new Position(getProtocolName()); - position.setDeviceId(deviceSession.getDeviceId()); + ByteBuf time = buf.readSlice(6); + DateBuilder dateBuilder = new DateBuilder() + .setYear(BcdUtil.readInteger(time, 2)) + .setMonth(BcdUtil.readInteger(time, 2)) + .setDay(BcdUtil.readInteger(time, 2)) + .setHour(BcdUtil.readInteger(time, 2)) + .setMinute(BcdUtil.readInteger(time, 2)) + .setSecond(BcdUtil.readInteger(time, 2)); + getLastLocation(position, dateBuilder.getDate()); - ByteBuf time = buf.readSlice(6); - DateBuilder dateBuilder = new DateBuilder() - .setYear(BcdUtil.readInteger(time, 2)) - .setMonth(BcdUtil.readInteger(time, 2)) - .setDay(BcdUtil.readInteger(time, 2)) - .setHour(BcdUtil.readInteger(time, 2)) - .setMinute(BcdUtil.readInteger(time, 2)) - .setSecond(BcdUtil.readInteger(time, 2)); - getLastLocation(position, dateBuilder.getDate()); - - Network network = new Network(); - - int wifiCount; - if (type == MSG_WIFI_4) { - wifiCount = buf.readUnsignedByte(); - } else { - wifiCount = buf.getUnsignedByte(2); - } + Network network = new Network(); - for (int i = 0; i < wifiCount; i++) { + int wifiCount; if (type == MSG_WIFI_4) { - buf.skipBytes(2); + wifiCount = buf.readUnsignedByte(); + } else { + wifiCount = buf.getUnsignedByte(2); } - WifiAccessPoint wifiAccessPoint = new WifiAccessPoint(); - wifiAccessPoint.setMacAddress(String.format("%02x:%02x:%02x:%02x:%02x:%02x", - buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte(), - buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())); - if (type != MSG_WIFI_4) { - wifiAccessPoint.setSignalStrength((int) buf.readUnsignedByte()); + + for (int i = 0; i < wifiCount; i++) { + if (type == MSG_WIFI_4) { + buf.skipBytes(2); + } + WifiAccessPoint wifiAccessPoint = new WifiAccessPoint(); + wifiAccessPoint.setMacAddress(String.format("%02x:%02x:%02x:%02x:%02x:%02x", + buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte(), + buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())); + if (type != MSG_WIFI_4) { + wifiAccessPoint.setSignalStrength((int) buf.readUnsignedByte()); + } + network.addWifiAccessPoint(wifiAccessPoint); } - network.addWifiAccessPoint(wifiAccessPoint); - } - if (type != MSG_WIFI_4) { + if (type != MSG_WIFI_4) { - int cellCount = buf.readUnsignedByte(); - int mcc = buf.readUnsignedShort(); - int mnc = buf.readUnsignedByte(); - for (int i = 0; i < cellCount; i++) { - network.addCellTower(CellTower.from( - mcc, mnc, buf.readUnsignedShort(), buf.readUnsignedShort(), buf.readUnsignedByte())); - } + int cellCount = buf.readUnsignedByte(); + int mcc = buf.readUnsignedShort(); + int mnc = buf.readUnsignedByte(); + for (int i = 0; i < cellCount; i++) { + network.addCellTower(CellTower.from( + mcc, mnc, buf.readUnsignedShort(), buf.readUnsignedShort(), buf.readUnsignedByte())); + } + + if (channel != null) { + ByteBuf response = Unpooled.buffer(); + response.writeShort(0x7878); + response.writeByte(0); + response.writeByte(type); + response.writeBytes(time.resetReaderIndex()); + response.writeByte('\r'); + response.writeByte('\n'); + channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress())); + } - if (channel != null) { - ByteBuf response = Unpooled.buffer(); - response.writeShort(0x7878); - response.writeByte(0); - response.writeByte(type); - response.writeBytes(time.resetReaderIndex()); - response.writeByte('\r'); - response.writeByte('\n'); - channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress())); } - } + position.setNetwork(network); - position.setNetwork(network); + return position; - return position; - } + } else if (type == MSG_INFO) { - private Object decodeBasicOther( - Channel channel, ByteBuf buf, DeviceSession deviceSession, int type, int dataLength) { + getLastLocation(position, null); - Position position = new Position(getProtocolName()); - position.setDeviceId(deviceSession.getDeviceId()); + position.set(Position.KEY_POWER, buf.readShort() * 0.01); + + return position; - if (type == MSG_LBS_STATUS && dataLength >= 18) { + } else if (type == MSG_LBS_MULTIPLE_3 && variant == Variant.SR411_MINI) { - return null; // space10x multi-lbs message + decodeGps(position, buf, false, deviceSession.get(DeviceSession.KEY_TIMEZONE)); + + decodeLbs(position, buf, type, false); + + position.set(Position.KEY_IGNITION, buf.readUnsignedByte() > 0); + position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01); + position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.01); + + return position; } else if (type == MSG_LBS_MULTIPLE_1 || type == MSG_LBS_MULTIPLE_2 || type == MSG_LBS_MULTIPLE_3 - || type == MSG_LBS_EXTEND || type == MSG_LBS_WIFI || type == MSG_LBS_2 + || type == MSG_LBS_EXTEND || type == MSG_LBS_WIFI || type == MSG_LBS_2 || type == MSG_LBS_3 || type == MSG_WIFI_3 || type == MSG_WIFI_5) { - boolean longFormat = type == MSG_LBS_2 || type == MSG_WIFI_3 || type == MSG_WIFI_5; - - DateBuilder dateBuilder = new DateBuilder(deviceSession.getTimeZone()) + DateBuilder dateBuilder = new DateBuilder((TimeZone) deviceSession.get(DeviceSession.KEY_TIMEZONE)) .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()) .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()); getLastLocation(position, dateBuilder.getDate()); - boolean hasCellCount = type == MSG_LBS_MULTIPLE_3 && dataLength == 44; - - if (hasCellCount) { + if (variant == Variant.WANWAY_S20 || variant == Variant.SL4X) { buf.readUnsignedByte(); // ta } int mcc = buf.readUnsignedShort(); - int mnc = BitUtil.check(mcc, 15) ? buf.readUnsignedShort() : buf.readUnsignedByte(); + int mnc = BitUtil.check(mcc, 15) || variant == Variant.SL4X + ? buf.readUnsignedShort() : buf.readUnsignedByte(); Network network = new Network(); - int cellCount = hasCellCount ? buf.readUnsignedByte() : type == MSG_WIFI_5 ? 6 : 7; + int cellCount = variant == Variant.WANWAY_S20 ? buf.readUnsignedByte() : type == MSG_WIFI_5 ? 6 : 7; for (int i = 0; i < cellCount; i++) { - int lac = longFormat ? buf.readInt() : buf.readUnsignedShort(); - int cid = longFormat ? (int) buf.readLong() : buf.readUnsignedMedium(); + int lac; + int cid; + if (type == MSG_LBS_2 || type == MSG_WIFI_3) { + lac = buf.readInt(); + cid = (int) buf.readLong(); + } else if (type == MSG_WIFI_5 || type == MSG_LBS_3) { + lac = buf.readUnsignedShort(); + cid = (int) buf.readUnsignedInt(); + } else { + lac = buf.readUnsignedShort(); + cid = buf.readUnsignedMedium(); + } int rssi = -buf.readUnsignedByte(); if (lac > 0) { network.addCellTower(CellTower.from(BitUtil.to(mcc, 15), mnc, lac, cid, rssi)); } } - if (!hasCellCount) { + if (variant != Variant.WANWAY_S20 && variant != Variant.SL4X) { buf.readUnsignedByte(); // ta } if (type != MSG_LBS_MULTIPLE_1 && type != MSG_LBS_MULTIPLE_2 && type != MSG_LBS_MULTIPLE_3 - && type != MSG_LBS_2) { + && type != MSG_LBS_2 && type != MSG_LBS_3) { int wifiCount = buf.readUnsignedByte(); for (int i = 0; i < wifiCount; i++) { String mac = ByteBufUtil.hexDump(buf.readSlice(6)).replaceAll("(..)", "$1:"); @@ -777,7 +754,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { } } - } else if (type == MSG_BMS || type == MSG_BMS_2) { + } else if (type == MSG_BMS) { buf.skipBytes(8); // serial number @@ -809,35 +786,199 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { return position; + } else if (type == MSG_STATUS && buf.readableBytes() == 22) { + + getLastLocation(position, null); + + buf.readUnsignedByte(); // information content + buf.readUnsignedShort(); // satellites + buf.readUnsignedByte(); // alarm + buf.readUnsignedByte(); // language + + position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte()); + + buf.readUnsignedByte(); // working mode + buf.readUnsignedShort(); // working voltage + buf.readUnsignedByte(); // reserved + buf.readUnsignedShort(); // working times + buf.readUnsignedShort(); // working time + + int value = buf.readUnsignedShort(); + double temperature = BitUtil.to(value, 15) * 0.1; + position.set(Position.PREFIX_TEMP + 1, BitUtil.check(value, 15) ? temperature : -temperature); + } else if (isSupported(type)) { - if (type == MSG_STATUS && buf.readableBytes() == 22) { - decodeHeartbeat(buf, position); + if (type == MSG_LBS_STATUS && variant == Variant.SPACE10X) { + return null; // multi-lbs message + } + + if (hasGps(type)) { + decodeGps(position, buf, false, deviceSession.get(DeviceSession.KEY_TIMEZONE)); } else { - decodeBasicUniversal(buf, deviceSession, type, position); + getLastLocation(position, null); + } + + if (hasLbs(type) && buf.readableBytes() > 6) { + boolean hasLength = hasStatus(type) + && type != MSG_LBS_STATUS + && type != MSG_LBS_ALARM + && (type != MSG_GPS_LBS_STATUS_1 || variant != Variant.VXT01); + decodeLbs(position, buf, type, hasLength); + } + + if (hasStatus(type)) { + decodeStatus(position, buf); + if (variant == Variant.OBD6) { + int signal = buf.readUnsignedShort(); + int satellites = BitUtil.between(signal, 10, 15) + BitUtil.between(signal, 5, 10); + position.set(Position.KEY_SATELLITES, satellites); + position.set(Position.KEY_RSSI, BitUtil.to(signal, 5)); + position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte())); + buf.readUnsignedByte(); // language + position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte()); + buf.readUnsignedByte(); // working mode + position.set(Position.KEY_POWER, buf.readUnsignedShort() / 100.0); + } else { + position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte() * 100 / 6); + position.set(Position.KEY_RSSI, buf.readUnsignedByte()); + short alarmExtension = buf.readUnsignedByte(); + if (variant != Variant.VXT01) { + position.set(Position.KEY_ALARM, decodeAlarm(alarmExtension)); + } + } + } + + if (type == MSG_GPS_LBS_1) { + if (variant == Variant.GT06E_CARD) { + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); + String data = buf.readCharSequence(buf.readUnsignedByte(), StandardCharsets.US_ASCII).toString(); + buf.readUnsignedByte(); // alarm + buf.readUnsignedByte(); // swiped + position.set(Position.KEY_CARD, data.trim()); + } else if (variant == Variant.BENWAY) { + int mask = buf.readUnsignedShort(); + position.set(Position.KEY_IGNITION, BitUtil.check(mask, 8 + 7)); + position.set(Position.PREFIX_IN + 2, BitUtil.check(mask, 8 + 6)); + if (BitUtil.check(mask, 8 + 4)) { + int value = BitUtil.to(mask, 8 + 1); + if (BitUtil.check(mask, 8 + 1)) { + value = -value; + } + position.set(Position.PREFIX_TEMP + 1, value); + } else { + int value = BitUtil.to(mask, 8 + 2); + if (BitUtil.check(mask, 8 + 5)) { + position.set(Position.PREFIX_ADC + 1, value); + } else { + position.set(Position.PREFIX_ADC + 1, value * 0.1); + } + } + } else if (variant == Variant.VXT01) { + decodeStatus(position, buf); + position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01); + position.set(Position.KEY_RSSI, buf.readUnsignedByte()); + buf.readUnsignedByte(); // alarm extension + } else if (variant == Variant.S5) { + decodeStatus(position, buf); + position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01); + position.set(Position.KEY_RSSI, buf.readUnsignedByte()); + position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte())); + position.set("oil", buf.readUnsignedShort()); + int temperature = buf.readUnsignedByte(); + if (BitUtil.check(temperature, 7)) { + temperature = -BitUtil.to(temperature, 7); + } + position.set(Position.PREFIX_TEMP + 1, temperature); + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 10); + } else if (variant == Variant.WETRUST) { + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); + position.set(Position.KEY_CARD, buf.readCharSequence( + buf.readUnsignedByte(), StandardCharsets.US_ASCII).toString()); + position.set(Position.KEY_ALARM, buf.readUnsignedByte() > 0 ? Position.ALARM_GENERAL : null); + position.set("cardStatus", buf.readUnsignedByte()); + position.set(Position.KEY_DRIVING_TIME, buf.readUnsignedShort()); + } + } + + if ((type == MSG_GPS_LBS_2 || type == MSG_GPS_LBS_3 || type == MSG_GPS_LBS_4) + && buf.readableBytes() >= 3 + 6) { + position.set(Position.KEY_IGNITION, buf.readUnsignedByte() > 0); + position.set(Position.KEY_EVENT, buf.readUnsignedByte()); // reason + position.set(Position.KEY_ARCHIVE, buf.readUnsignedByte() > 0); + } + + if (type == MSG_GPS_LBS_3) { + int module = buf.readUnsignedShort(); + int subLength = buf.readUnsignedByte(); + switch (module) { + case 0x0027: + position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01); + break; + case 0x002E: + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); + break; + case 0x003B: + position.setAccuracy(buf.readUnsignedShort() * 0.01); + break; + default: + buf.skipBytes(subLength); + break; + } + } + + if (buf.readableBytes() == 3 + 6 || buf.readableBytes() == 3 + 4 + 6) { + position.set(Position.KEY_IGNITION, buf.readUnsignedByte() > 0); + buf.readUnsignedByte(); // upload mode + position.set(Position.KEY_ARCHIVE, buf.readUnsignedByte() > 0 ? true : null); + } + + if (buf.readableBytes() == 4 + 6) { + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); } } else if (type == MSG_ALARM) { + boolean extendedAlarm = dataLength > 7; if (extendedAlarm) { - decodeGps(position, buf, false, false, false, deviceSession.getTimeZone()); + if (variant == Variant.JC400) { + buf.readUnsignedShort(); // marker + buf.readUnsignedByte(); // version + } + decodeGps( + position, buf, false, + variant == Variant.JC400, variant == Variant.JC400, variant == Variant.JC400, + deviceSession.get(DeviceSession.KEY_TIMEZONE)); } else { - DateBuilder dateBuilder = new DateBuilder(deviceSession.getTimeZone()) + DateBuilder dateBuilder = new DateBuilder((TimeZone) deviceSession.get(DeviceSession.KEY_TIMEZONE)) .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()) .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()); getLastLocation(position, dateBuilder.getDate()); } - short alarmType = buf.readUnsignedByte(); - switch (alarmType) { + if (variant == Variant.JC400) { + position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.1); + } + short event = buf.readUnsignedByte(); + position.set(Position.KEY_EVENT, event); + switch (event) { case 0x01: position.set(Position.KEY_ALARM, extendedAlarm ? Position.ALARM_SOS : Position.ALARM_GENERAL); break; + case 0x0E: + position.set(Position.KEY_ALARM, Position.ALARM_LOW_POWER); + break; + case 0x76: + position.set(Position.KEY_ALARM, Position.ALARM_TEMPERATURE); + break; case 0x80: position.set(Position.KEY_ALARM, Position.ALARM_VIBRATION); break; case 0x87: position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED); break; + case 0x88: + position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT); + break; case 0x90: position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION); break; @@ -851,9 +992,9 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_ALARM, Position.ALARM_ACCIDENT); break; default: - position.set(Position.KEY_ALARM, Position.ALARM_GENERAL); break; } + } else { if (dataLength > 0) { @@ -879,117 +1020,6 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { return position; } - private void decodeHeartbeat(ByteBuf buf, Position position) { - - getLastLocation(position, null); - - buf.readUnsignedByte(); // information content - buf.readUnsignedShort(); // satellites - buf.readUnsignedByte(); // alarm - buf.readUnsignedByte(); // language - - position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte()); - - buf.readUnsignedByte(); // working mode - buf.readUnsignedShort(); // working voltage - buf.readUnsignedByte(); // reserved - buf.readUnsignedShort(); // working times - buf.readUnsignedShort(); // working time - - int value = buf.readUnsignedShort(); - double temperature = BitUtil.to(value, 15) * 0.1; - position.set(Position.PREFIX_TEMP + 1, BitUtil.check(value, 15) ? temperature : -temperature); - - } - - private void decodeBasicUniversal(ByteBuf buf, DeviceSession deviceSession, int type, Position position) { - - if (hasGps(type)) { - decodeGps(position, buf, false, deviceSession.getTimeZone()); - } else { - getLastLocation(position, null); - } - - if (hasLbs(type)) { - decodeLbs(position, buf, type, hasStatus(type)); - } - - if (hasStatus(type)) { - decodeStatus(position, buf, true); - position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte())); - } - - if (type == MSG_GPS_LBS_1) { - if (buf.readableBytes() > 75 + 6) { - position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); - String data = buf.readCharSequence(buf.readUnsignedByte(), StandardCharsets.US_ASCII).toString(); - buf.readUnsignedByte(); // alarm - buf.readUnsignedByte(); // swiped - position.set("driverLicense", data.trim()); - } else if (buf.readableBytes() == 8) { - int mask = buf.readUnsignedShort(); - position.set(Position.KEY_IGNITION, BitUtil.check(mask, 8 + 7)); - position.set(Position.PREFIX_IN + 2, BitUtil.check(mask, 8 + 6)); - if (BitUtil.check(mask, 8 + 4)) { - int value = BitUtil.to(mask, 8 + 1); - if (BitUtil.check(mask, 8 + 1)) { - value = -value; - } - position.set(Position.PREFIX_TEMP + 1, value); - } else { - int value = BitUtil.to(mask, 8 + 2); - if (BitUtil.check(mask, 8 + 5)) { - position.set(Position.PREFIX_ADC + 1, value); - } else { - position.set(Position.PREFIX_ADC + 1, value * 0.1); - } - } - } else if (buf.readableBytes() == 11) { - decodeStatus(position, buf, false); - buf.readUnsignedByte(); // alarm extension - } else if (buf.readableBytes() == 18) { - decodeStatus(position, buf, false); - position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte())); - position.set("oil", buf.readUnsignedShort()); - int temperature = buf.readUnsignedByte(); - if (BitUtil.check(temperature, 7)) { - temperature = -BitUtil.to(temperature, 7); - } - position.set(Position.PREFIX_TEMP + 1, temperature); - position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 10); - } - } - - if ((type == MSG_GPS_LBS_2 || type == MSG_GPS_LBS_3 || type == MSG_GPS_LBS_4) && buf.readableBytes() >= 3 + 6) { - position.set(Position.KEY_IGNITION, buf.readUnsignedByte() > 0); - position.set(Position.KEY_EVENT, buf.readUnsignedByte()); // reason - position.set(Position.KEY_ARCHIVE, buf.readUnsignedByte() > 0); - } - - if (type == MSG_GPS_LBS_3) { - int module = buf.readUnsignedShort(); - int length = buf.readUnsignedByte(); - switch (module) { - case 0x0027: - position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01); - break; - case 0x002E: - position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); - break; - case 0x003B: - position.setAccuracy(buf.readUnsignedShort() * 0.01); - break; - default: - buf.skipBytes(length); - break; - } - } - - if (buf.readableBytes() == 4 + 6) { - position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); - } - } - private Object decodeExtended(Channel channel, SocketAddress remoteAddress, ByteBuf buf) { DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); @@ -997,8 +1027,8 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { return null; } - if (deviceSession.getTimeZone() == null) { - deviceSession.setTimeZone(getTimeZone(deviceSession.getDeviceId())); + if (!deviceSession.contains(DeviceSession.KEY_TIMEZONE)) { + deviceSession.set(DeviceSession.KEY_TIMEZONE, getTimeZone(deviceSession.getDeviceId())); } Position position = new Position(getProtocolName()); @@ -1017,7 +1047,16 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { data = buf.readSlice(buf.readableBytes() - 6).toString(StandardCharsets.UTF_16BE); } - if (decodeLocationString(position, data) == null) { + Parser parser = new Parser(PATTERN_LOCATION, data); + + if (parser.matches()) { + position.setValid(true); + position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG)); + position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG)); + position.setCourse(parser.nextDouble()); + position.setSpeed(parser.nextDouble()); + position.setTime(parser.nextDateTime(Parser.DateTimeFormat.YMD_HMS)); + } else { getLastLocation(position, null); position.set(Position.KEY_RESULT, data); } @@ -1031,31 +1070,85 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { getLastLocation(position, null); if (subType == 0x00) { + position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort() * 0.01); return position; + + } else if (subType == 0x04) { + + CharSequence content = buf.readCharSequence(buf.readableBytes() - 4 - 2, StandardCharsets.US_ASCII); + String[] values = content.toString().split(";"); + for (String value : values) { + String[] pair = value.split("="); + switch (pair[0]) { + case "ALM1": + case "ALM2": + case "ALM3": + position.set("alarm" + pair[0].charAt(3) + "Status", Integer.parseInt(pair[1], 16)); + case "STA1": + position.set("otherStatus", Integer.parseInt(pair[1], 16)); + break; + case "DYD": + position.set("engineStatus", Integer.parseInt(pair[1], 16)); + break; + default: + break; + } + } + return position; + } else if (subType == 0x05) { + + if (buf.readableBytes() >= 6 + 1 + 6) { + DateBuilder dateBuilder = new DateBuilder((TimeZone) deviceSession.get(DeviceSession.KEY_TIMEZONE)) + .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()) + .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()); + position.setDeviceTime(dateBuilder.getDate()); + } + int flags = buf.readUnsignedByte(); position.set(Position.KEY_DOOR, BitUtil.check(flags, 0)); position.set(Position.PREFIX_IO + 1, BitUtil.check(flags, 2)); return position; + } else if (subType == 0x0a) { + buf.skipBytes(8); // imei buf.skipBytes(8); // imsi position.set(Position.KEY_ICCID, ByteBufUtil.hexDump(buf.readSlice(10)).replaceAll("f", "")); return position; + } else if (subType == 0x0d) { + if (buf.getByte(buf.readerIndex()) != '!') { buf.skipBytes(6); } - return decodeFuelData(position, buf.toString( + + Parser parser = new Parser(PATTERN_FUEL, buf.toString( buf.readerIndex(), buf.readableBytes() - 4 - 2, StandardCharsets.US_ASCII)); + if (!parser.matches()) { + return null; + } + + position.set(Position.PREFIX_TEMP + 1, parser.nextDouble(0)); + position.set(Position.KEY_FUEL_LEVEL, parser.nextDouble(0)); + + return position; + } else if (subType == 0x1b) { - buf.readUnsignedByte(); // header - buf.readUnsignedByte(); // type - position.set(Position.KEY_DRIVER_UNIQUE_ID, ByteBufUtil.hexDump(buf.readSlice(4))); - buf.readUnsignedByte(); // checksum - buf.readUnsignedByte(); // footer + + if (Character.isLetter(buf.getUnsignedByte(buf.readerIndex()))) { + String data = buf.readCharSequence(buf.readableBytes() - 6, StandardCharsets.US_ASCII).toString(); + position.set("serial", data.trim()); + } else { + buf.readUnsignedByte(); // header + buf.readUnsignedByte(); // type + position.set(Position.KEY_DRIVER_UNIQUE_ID, ByteBufUtil.hexDump(buf.readSlice(4))); + buf.readUnsignedByte(); // checksum + buf.readUnsignedByte(); // footer + } return position; + } } else if (type == MSG_X1_PHOTO_DATA) { @@ -1070,15 +1163,13 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { if (photo.writableBytes() > 0) { sendPhotoRequest(channel, pictureId); } else { - Device device = Context.getDeviceManager().getById(deviceSession.getDeviceId()); - position.set( - Position.KEY_IMAGE, Context.getMediaManager().writeFile(device.getUniqueId(), photo, "jpg")); + position.set(Position.KEY_IMAGE, writeMediaFile(deviceSession.getUniqueId(), photo, "jpg")); photos.remove(pictureId).release(); } } else if (type == MSG_AZ735_GPS || type == MSG_AZ735_ALARM) { - if (!decodeGps(position, buf, true, deviceSession.getTimeZone())) { + if (!decodeGps(position, buf, true, deviceSession.get(DeviceSession.KEY_TIMEZONE))) { getLastLocation(position, position.getDeviceTime()); } @@ -1123,7 +1214,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { } else if (type == MSG_OBD) { - DateBuilder dateBuilder = new DateBuilder(deviceSession.getTimeZone()) + DateBuilder dateBuilder = new DateBuilder((TimeZone) deviceSession.get(DeviceSession.KEY_TIMEZONE)) .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()) .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()); @@ -1170,132 +1261,111 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { } else if (type == MSG_GPS_MODULAR) { - return decodeExtendedModular(channel, buf, deviceSession); + while (buf.readableBytes() > 6) { + int moduleType = buf.readUnsignedShort(); + int moduleLength = buf.readUnsignedShort(); - } else { - - return decodeExtendedOther(channel, buf, deviceSession, type); - - } - - return null; - } - - private Object decodeExtendedModular(Channel channel, ByteBuf buf, DeviceSession deviceSession) { - - Position position = new Position(getProtocolName()); - position.setDeviceId(deviceSession.getDeviceId()); - - while (buf.readableBytes() > 6) { - int moduleType = buf.readUnsignedShort(); - int moduleLength = buf.readUnsignedShort(); - - switch (moduleType) { - case 0x03: - position.set(Position.KEY_ICCID, ByteBufUtil.hexDump(buf.readSlice(10))); - break; - case 0x09: - position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); - break; - case 0x0a: - position.set(Position.KEY_SATELLITES_VISIBLE, buf.readUnsignedByte()); - break; - case 0x11: - CellTower cellTower = CellTower.from( - buf.readUnsignedShort(), - buf.readUnsignedShort(), - buf.readUnsignedShort(), - buf.readUnsignedMedium(), - buf.readUnsignedByte()); - if (cellTower.getCellId() > 0) { - position.setNetwork(new Network(cellTower)); - } - break; - case 0x18: - position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.01); - break; - case 0x28: - position.set(Position.KEY_HDOP, buf.readUnsignedByte() * 0.1); - break; - case 0x29: - position.set(Position.KEY_INDEX, buf.readUnsignedInt()); - break; - case 0x2a: - int input = buf.readUnsignedByte(); - position.set(Position.KEY_DOOR, BitUtil.to(input, 4) > 0); - position.set("tamper", BitUtil.from(input, 4) > 0); - break; - case 0x2b: - int event = buf.readUnsignedByte(); - switch (event) { - case 0x11: - position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY); - break; - case 0x12: - position.set(Position.KEY_ALARM, Position.ALARM_LOW_POWER); - break; - case 0x13: - position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT); - break; - case 0x14: - position.set(Position.KEY_ALARM, Position.ALARM_REMOVING); - break; - default: - break; - } - position.set(Position.KEY_EVENT, event); - break; - case 0x2e: - position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE()); - break; - case 0x33: - position.setTime(new Date(buf.readUnsignedInt() * 1000)); - position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); - position.setAltitude(buf.readShort()); - - double latitude = buf.readUnsignedInt() / 60.0 / 30000.0; - double longitude = buf.readUnsignedInt() / 60.0 / 30000.0; - position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte())); - - int flags = buf.readUnsignedShort(); - position.setCourse(BitUtil.to(flags, 10)); - position.setValid(BitUtil.check(flags, 12)); - - if (!BitUtil.check(flags, 10)) { - latitude = -latitude; - } - if (BitUtil.check(flags, 11)) { - longitude = -longitude; - } - - position.setLatitude(latitude); - position.setLongitude(longitude); - break; - case 0x34: - position.set(Position.KEY_EVENT, buf.readUnsignedByte()); - buf.readUnsignedIntLE(); // time - buf.skipBytes(buf.readUnsignedByte()); // content - break; - default: - buf.skipBytes(moduleLength); - break; + switch (moduleType) { + case 0x03: + position.set(Position.KEY_ICCID, ByteBufUtil.hexDump(buf.readSlice(10))); + break; + case 0x09: + position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); + break; + case 0x0a: + position.set(Position.KEY_SATELLITES_VISIBLE, buf.readUnsignedByte()); + break; + case 0x11: + CellTower cellTower = CellTower.from( + buf.readUnsignedShort(), + buf.readUnsignedShort(), + buf.readUnsignedShort(), + buf.readUnsignedMedium(), + buf.readUnsignedByte()); + if (cellTower.getCellId() > 0) { + position.setNetwork(new Network(cellTower)); + } + break; + case 0x18: + position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.01); + break; + case 0x28: + position.set(Position.KEY_HDOP, buf.readUnsignedByte() * 0.1); + break; + case 0x29: + position.set(Position.KEY_INDEX, buf.readUnsignedInt()); + break; + case 0x2a: + int input = buf.readUnsignedByte(); + position.set(Position.KEY_DOOR, BitUtil.to(input, 4) > 0); + position.set("tamper", BitUtil.from(input, 4) > 0); + break; + case 0x2b: + int event = buf.readUnsignedByte(); + switch (event) { + case 0x11: + position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY); + break; + case 0x12: + position.set(Position.KEY_ALARM, Position.ALARM_LOW_POWER); + break; + case 0x13: + position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT); + break; + case 0x14: + position.set(Position.KEY_ALARM, Position.ALARM_REMOVING); + break; + default: + break; + } + position.set(Position.KEY_EVENT, event); + break; + case 0x2e: + position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE()); + break; + case 0x33: + position.setTime(new Date(buf.readUnsignedInt() * 1000)); + position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); + position.setAltitude(buf.readShort()); + + double latitude = buf.readUnsignedInt() / 60.0 / 30000.0; + double longitude = buf.readUnsignedInt() / 60.0 / 30000.0; + position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte())); + + int flags = buf.readUnsignedShort(); + position.setCourse(BitUtil.to(flags, 10)); + position.setValid(BitUtil.check(flags, 12)); + + if (!BitUtil.check(flags, 10)) { + latitude = -latitude; + } + if (BitUtil.check(flags, 11)) { + longitude = -longitude; + } + + position.setLatitude(latitude); + position.setLongitude(longitude); + break; + case 0x34: + position.set(Position.KEY_EVENT, buf.readUnsignedByte()); + buf.readUnsignedIntLE(); // time + buf.skipBytes(buf.readUnsignedByte()); // content + break; + default: + buf.skipBytes(moduleLength); + break; + } } - } - - if (position.getFixTime() == null) { - getLastLocation(position, null); - } - - sendResponse(channel, false, MSG_GPS_MODULAR, buf.readUnsignedShort(), null); - return position; - } + if (position.getFixTime() == null) { + getLastLocation(position, null); + } - private Object decodeExtendedOther(Channel channel, ByteBuf buf, DeviceSession deviceSession, int type) { + sendResponse(channel, false, MSG_GPS_MODULAR, buf.readUnsignedShort(), null); - Position position = null; + return position; - if (type == MSG_MULTIMEDIA || type == MSG_MULTIMEDIA_2) { + } else if (type == MSG_MULTIMEDIA) { buf.skipBytes(8); // serial number long timestamp = buf.readUnsignedInt() * 1000; @@ -1328,9 +1398,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); getLastLocation(position, new Date(timestamp)); - Device device = Context.getDeviceManager().getById(deviceSession.getDeviceId()); - position.set(Position.KEY_IMAGE, - Context.getMediaManager().writeFile(device.getUniqueId(), photo, "jpg")); + position.set(Position.KEY_IMAGE, writeMediaFile(deviceSession.getUniqueId(), photo, "jpg")); photos.remove(mediaId).release(); } } @@ -1348,19 +1416,13 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { getLastLocation(position, null); buf.readUnsignedByte(); // external device type code - int length = buf.readableBytes() - 9; // line break + checksum + index + checksum + footer - if (length <= 0) { - return null; - } else if (length < 8) { - position.set( - Position.PREFIX_TEMP + 1, - Double.parseDouble(buf.readCharSequence(length - 1, StandardCharsets.US_ASCII).toString())); + ByteBuf data = buf.readSlice(buf.readableBytes() - 6); // index + checksum + footer + if (BufferUtil.isPrintable(data, data.readableBytes())) { + String value = data.readCharSequence(data.readableBytes(), StandardCharsets.US_ASCII).toString(); + position.set(Position.KEY_RESULT, value.trim()); } else { - buf.readUnsignedByte(); // card type - position.set( - Position.KEY_DRIVER_UNIQUE_ID, - buf.readCharSequence(length - 1, StandardCharsets.US_ASCII).toString()); + position.set(Position.KEY_RESULT, ByteBufUtil.hexDump(data)); } return position; @@ -1386,6 +1448,26 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { variant = Variant.VXT01; } else if (header == 0x7878 && type == MSG_GPS_LBS_STATUS_1 && length == 0x24) { variant = Variant.VXT01; + } else if (header == 0x7878 && type == MSG_LBS_MULTIPLE_3 && length == 0x31) { + variant = Variant.WANWAY_S20; + } else if (header == 0x7878 && type == MSG_LBS_MULTIPLE_3 && length == 0x2e) { + variant = Variant.SR411_MINI; + } else if (header == 0x7878 && type == MSG_GPS_LBS_1 && length >= 0x71) { + variant = Variant.GT06E_CARD; + } else if (header == 0x7878 && type == MSG_GPS_LBS_1 && length == 0x21) { + variant = Variant.BENWAY; + } else if (header == 0x7878 && type == MSG_GPS_LBS_1 && length == 0x2b) { + variant = Variant.S5; + } else if (header == 0x7878 && type == MSG_LBS_STATUS && length >= 0x17) { + variant = Variant.SPACE10X; + } else if (header == 0x7878 && type == MSG_STATUS && length == 0x13) { + variant = Variant.OBD6; + } else if (header == 0x7878 && type == MSG_GPS_LBS_1 && length == 0x29) { + variant = Variant.WETRUST; + } else if (header == 0x7878 && type == MSG_ALARM && buf.getUnsignedShort(buf.readerIndex() + 4) == 0xffff) { + variant = Variant.JC400; + } else if (header == 0x7878 && type == MSG_LBS_3 && length == 0x37) { + variant = Variant.SL4X; } else { variant = Variant.STANDARD; } diff --git a/src/main/java/org/traccar/protocol/Gt06ProtocolEncoder.java b/src/main/java/org/traccar/protocol/Gt06ProtocolEncoder.java index 9115ba10f..dc5dd446f 100644 --- a/src/main/java/org/traccar/protocol/Gt06ProtocolEncoder.java +++ b/src/main/java/org/traccar/protocol/Gt06ProtocolEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org) + * 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. @@ -18,10 +18,12 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import org.traccar.BaseProtocolEncoder; -import org.traccar.Context; +import org.traccar.Protocol; +import org.traccar.config.Keys; import org.traccar.helper.Checksum; +import org.traccar.helper.model.AttributeUtil; import org.traccar.model.Command; -import org.traccar.Protocol; +import org.traccar.model.Device; import java.nio.charset.StandardCharsets; @@ -33,8 +35,8 @@ public class Gt06ProtocolEncoder extends BaseProtocolEncoder { private ByteBuf encodeContent(long deviceId, String content) { - boolean language = Context.getIdentityManager() - .lookupAttributeBoolean(deviceId, getProtocolName() + ".language", false, false, true); + boolean language = AttributeUtil.lookup( + getCacheManager(), Keys.PROTOCOL_LANGUAGE.withPrefix(getProtocolName()), deviceId); ByteBuf buf = Unpooled.buffer(); @@ -43,7 +45,7 @@ public class Gt06ProtocolEncoder extends BaseProtocolEncoder { buf.writeByte(1 + 1 + 4 + content.length() + 2 + 2 + (language ? 2 : 0)); // message length - buf.writeByte(0x80); // message type + buf.writeByte(Gt06ProtocolDecoder.MSG_COMMAND_0); buf.writeByte(4 + content.length()); // command length buf.writeInt(0); @@ -66,19 +68,31 @@ public class Gt06ProtocolEncoder extends BaseProtocolEncoder { @Override protected Object encodeCommand(Command command) { - boolean alternative = Context.getIdentityManager().lookupAttributeBoolean( - command.getDeviceId(), getProtocolName() + ".alternative", false, false, true); + boolean alternative = AttributeUtil.lookup( + getCacheManager(), Keys.PROTOCOL_ALTERNATIVE.withPrefix(getProtocolName()), command.getDeviceId()); + + String password = AttributeUtil.getDevicePassword( + getCacheManager(), command.getDeviceId(), getProtocolName(), "123456"); - String password = Context.getIdentityManager() - .getDevicePassword(command.getDeviceId(), getProtocolName(), "123456"); + Device device = getCacheManager().getObject(Device.class, command.getDeviceId()); switch (command.getType()) { case Command.TYPE_ENGINE_STOP: - return encodeContent(command.getDeviceId(), - alternative ? "DYD," + password + "#" : "Relay,1#"); + if ("G109".equals(device.getModel())) { + return encodeContent(command.getDeviceId(), "DYD#"); + } else if (alternative) { + return encodeContent(command.getDeviceId(), "DYD," + password + "#"); + } else { + return encodeContent(command.getDeviceId(), "Relay,1#"); + } case Command.TYPE_ENGINE_RESUME: - return encodeContent(command.getDeviceId(), - alternative ? "HFYD," + password + "#" : "Relay,0#"); + if ("G109".equals(device.getModel())) { + return encodeContent(command.getDeviceId(), "HFYD#"); + } else if (alternative) { + return encodeContent(command.getDeviceId(), "HFYD," + password + "#"); + } else { + return encodeContent(command.getDeviceId(), "Relay,0#"); + } case Command.TYPE_CUSTOM: return encodeContent(command.getDeviceId(), command.getString(Command.KEY_DATA)); default: diff --git a/src/main/java/org/traccar/protocol/Gt30Protocol.java b/src/main/java/org/traccar/protocol/Gt30Protocol.java index aa4ad20b1..fdfc80502 100644 --- a/src/main/java/org/traccar/protocol/Gt30Protocol.java +++ b/src/main/java/org/traccar/protocol/Gt30Protocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class Gt30Protocol extends BaseProtocol { - public Gt30Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public Gt30Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/Gt30ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt30ProtocolDecoder.java index abf208a46..fb3a2b8ae 100644 --- a/src/main/java/org/traccar/protocol/Gt30ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gt30ProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/H02Protocol.java b/src/main/java/org/traccar/protocol/H02Protocol.java index a5246abc6..ba5aeaa26 100644 --- a/src/main/java/org/traccar/protocol/H02Protocol.java +++ b/src/main/java/org/traccar/protocol/H02Protocol.java @@ -17,15 +17,18 @@ package org.traccar.protocol; import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; -import org.traccar.Context; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class H02Protocol extends BaseProtocol { - public H02Protocol() { + @Inject + public H02Protocol(Config config) { setSupportedDataCommands( Command.TYPE_ALARM_ARM, Command.TYPE_ALARM_DISARM, @@ -33,19 +36,19 @@ public class H02Protocol extends BaseProtocol { Command.TYPE_ENGINE_RESUME, Command.TYPE_POSITION_PERIODIC ); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { - int messageLength = Context.getConfig().getInteger(Keys.PROTOCOL_MESSAGE_LENGTH.withPrefix(getName())); + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + int messageLength = config.getInteger(Keys.PROTOCOL_MESSAGE_LENGTH.withPrefix(getName())); pipeline.addLast(new H02FrameDecoder(messageLength)); pipeline.addLast(new StringEncoder()); pipeline.addLast(new H02ProtocolEncoder(H02Protocol.this)); pipeline.addLast(new H02ProtocolDecoder(H02Protocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringEncoder()); pipeline.addLast(new H02ProtocolEncoder(H02Protocol.this)); pipeline.addLast(new H02ProtocolDecoder(H02Protocol.this)); diff --git a/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java b/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java index 10a272bff..2ad4f644b 100644 --- a/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java @@ -19,8 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.config.Keys; @@ -334,7 +333,7 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder { if (parser.hasNext() && parser.next().equals("V1")) { sendResponse(channel, remoteAddress, id, "V1"); - } else if (Context.getConfig().getBoolean(Keys.PROTOCOL_ACK.withPrefix(getProtocolName()))) { + } else if (getConfig().getBoolean(Keys.PROTOCOL_ACK.withPrefix(getProtocolName()))) { sendResponse(channel, remoteAddress, id, "R12"); } @@ -392,7 +391,8 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder { position.setAltitude(parser.nextInt(0)); - position.setNetwork(new Network(CellTower.fromLacCid(parser.nextHexInt(0), parser.nextHexInt(0)))); + position.setNetwork(new Network(CellTower.fromLacCid( + getConfig(), parser.nextHexInt(0), parser.nextHexInt(0)))); } if (parser.hasNext()) { diff --git a/src/main/java/org/traccar/protocol/H02ProtocolEncoder.java b/src/main/java/org/traccar/protocol/H02ProtocolEncoder.java index 8f1a8c042..86b8c80d4 100644 --- a/src/main/java/org/traccar/protocol/H02ProtocolEncoder.java +++ b/src/main/java/org/traccar/protocol/H02ProtocolEncoder.java @@ -16,10 +16,11 @@ */ package org.traccar.protocol; -import org.traccar.Context; +import org.traccar.Protocol; import org.traccar.StringProtocolEncoder; +import org.traccar.config.Keys; +import org.traccar.helper.model.AttributeUtil; import org.traccar.model.Command; -import org.traccar.Protocol; import java.util.Date; @@ -59,8 +60,9 @@ public class H02ProtocolEncoder extends StringProtocolEncoder { return formatCommand(time, uniqueId, "S20", "1", "0"); case Command.TYPE_POSITION_PERIODIC: String frequency = command.getAttributes().get(Command.KEY_FREQUENCY).toString(); - if (Context.getIdentityManager().lookupAttributeBoolean( - command.getDeviceId(), getProtocolName() + ".alternative", false, false, true)) { + if (AttributeUtil.lookup( + getCacheManager(), Keys.PROTOCOL_ALTERNATIVE.withPrefix(getProtocolName()), + command.getDeviceId())) { return formatCommand(time, uniqueId, "D1", frequency); } else { return formatCommand(time, uniqueId, "S71", "22", frequency); diff --git a/src/main/java/org/traccar/protocol/HaicomProtocol.java b/src/main/java/org/traccar/protocol/HaicomProtocol.java index 6e5760bd4..bcc491ada 100644 --- a/src/main/java/org/traccar/protocol/HaicomProtocol.java +++ b/src/main/java/org/traccar/protocol/HaicomProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class HaicomProtocol extends BaseProtocol { - public HaicomProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public HaicomProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '*')); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/HaicomProtocolDecoder.java b/src/main/java/org/traccar/protocol/HaicomProtocolDecoder.java index dd20f2aeb..9903e7735 100644 --- a/src/main/java/org/traccar/protocol/HaicomProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/HaicomProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.BitUtil; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/HomtecsProtocol.java b/src/main/java/org/traccar/protocol/HomtecsProtocol.java index 34dbf0f51..c04efb945 100644 --- a/src/main/java/org/traccar/protocol/HomtecsProtocol.java +++ b/src/main/java/org/traccar/protocol/HomtecsProtocol.java @@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class HomtecsProtocol extends BaseProtocol { - public HomtecsProtocol() { - addServer(new TrackerServer(true, getName()) { + @Inject + public HomtecsProtocol(Config config) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new HomtecsProtocolDecoder(HomtecsProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/HomtecsProtocolDecoder.java b/src/main/java/org/traccar/protocol/HomtecsProtocolDecoder.java index a93572b5c..5541cb065 100644 --- a/src/main/java/org/traccar/protocol/HomtecsProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/HomtecsProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/HoopoProtocol.java b/src/main/java/org/traccar/protocol/HoopoProtocol.java index 387b967d3..3fc0887d8 100644 --- a/src/main/java/org/traccar/protocol/HoopoProtocol.java +++ b/src/main/java/org/traccar/protocol/HoopoProtocol.java @@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class HoopoProtocol extends BaseProtocol { - public HoopoProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public HoopoProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new JsonFrameDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/HoopoProtocolDecoder.java b/src/main/java/org/traccar/protocol/HoopoProtocolDecoder.java index af51a99c6..7433e7fce 100644 --- a/src/main/java/org/traccar/protocol/HoopoProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/HoopoProtocolDecoder.java @@ -17,12 +17,12 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.model.Position; -import javax.json.Json; -import javax.json.JsonObject; +import jakarta.json.Json; +import jakarta.json.JsonObject; import java.io.StringReader; import java.net.SocketAddress; import java.time.OffsetDateTime; diff --git a/src/main/java/org/traccar/protocol/HuaShengProtocol.java b/src/main/java/org/traccar/protocol/HuaShengProtocol.java index 103f2d501..7246e97e6 100644 --- a/src/main/java/org/traccar/protocol/HuaShengProtocol.java +++ b/src/main/java/org/traccar/protocol/HuaShengProtocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 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. @@ -18,14 +18,26 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; +import org.traccar.model.Command; + +import jakarta.inject.Inject; public class HuaShengProtocol extends BaseProtocol { - public HuaShengProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public HuaShengProtocol(Config config) { + setSupportedDataCommands( + Command.TYPE_POSITION_PERIODIC, + Command.TYPE_OUTPUT_CONTROL, + Command.TYPE_ALARM_ARM, + Command.TYPE_ALARM_DISARM, + Command.TYPE_SET_SPEED_LIMIT); + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new HuaShengFrameDecoder()); + pipeline.addLast(new HuaShengProtocolEncoder(HuaShengProtocol.this)); pipeline.addLast(new HuaShengProtocolDecoder(HuaShengProtocol.this)); } }); diff --git a/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java index 891046213..7d634b0f2 100644 --- a/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 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. @@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; @@ -48,6 +48,10 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_UPFAULT_RSP = 0xFF13; public static final int MSG_HSO_REQ = 0x0002; public static final int MSG_HSO_RSP = 0x0003; + public static final int MSG_SET_REQ = 0xAA04; + public static final int MSG_SET_RSP = 0xFF05; + public static final int MSG_CTRL_REQ = 0xAA16; + public static final int MSG_CTRL_RSP = 0xFF17; private void sendResponse(Channel channel, int type, int index, ByteBuf content) { if (channel != null) { @@ -225,13 +229,14 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder { position.setCourse(buf.readUnsignedShort()); position.setAltitude(buf.readUnsignedShort()); - position.set(Position.KEY_ODOMETER, buf.readUnsignedShort() * 1000); + buf.readUnsignedShort(); // odometer speed Network network = new Network(); while (buf.readableBytes() > 4) { int subtype = buf.readUnsignedShort(); int length = buf.readUnsignedShort() - 4; + int endIndex = buf.readerIndex() + length; switch (subtype) { case 0x0001: int coolantTemperature = buf.readUnsignedByte() - 40; @@ -249,6 +254,9 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01); position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedByte() * 0.4); buf.readUnsignedInt(); // trip id + if (buf.readerIndex() < endIndex) { + position.set("adBlueLevel", buf.readUnsignedByte() * 0.4); + } break; case 0x0005: position.set(Position.KEY_RSSI, buf.readUnsignedByte()); @@ -256,8 +264,11 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedInt(); // run time break; case 0x0009: - position.set( - Position.KEY_VIN, buf.readCharSequence(length, StandardCharsets.US_ASCII).toString()); + position.set(Position.KEY_VIN, buf.readCharSequence(length, StandardCharsets.US_ASCII).toString()); + break; + case 0x0010: + position.set(Position.KEY_ODOMETER, Double.parseDouble( + buf.readCharSequence(length, StandardCharsets.US_ASCII).toString()) * 1000); break; case 0x0011: position.set(Position.KEY_HOURS, buf.readUnsignedInt() * 0.05); @@ -276,7 +287,7 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder { String[] values = cell.split("@"); network.addCellTower(CellTower.from( Integer.parseInt(values[0]), Integer.parseInt(values[1]), - Integer.parseInt(values[2], 16), Integer.parseInt(values[3], 16))); + Integer.parseInt(values[2], 16), Long.parseLong(values[3], 16))); } break; case 0x0021: @@ -291,6 +302,7 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder { buf.skipBytes(length); break; } + buf.readerIndex(endIndex); } if (network.getCellTowers() != null || network.getWifiAccessPoints() != null) { diff --git a/src/main/java/org/traccar/protocol/HuaShengProtocolEncoder.java b/src/main/java/org/traccar/protocol/HuaShengProtocolEncoder.java new file mode 100644 index 000000000..dc34f7b4e --- /dev/null +++ b/src/main/java/org/traccar/protocol/HuaShengProtocolEncoder.java @@ -0,0 +1,85 @@ +/* + * 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.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import org.traccar.BaseProtocolEncoder; +import org.traccar.Protocol; +import org.traccar.model.Command; + +public class HuaShengProtocolEncoder extends BaseProtocolEncoder { + + public HuaShengProtocolEncoder(Protocol protocol) { + super(protocol); + } + + private ByteBuf encodeContent(int type, ByteBuf content) { + + ByteBuf buf = Unpooled.buffer(); + buf.writeByte(0xC0); + buf.writeShort(0x0000); // flag and version + buf.writeShort(12 + content.readableBytes()); + buf.writeShort(type); + buf.writeShort(0); // checksum + buf.writeInt(1); // index + buf.writeBytes(content); + content.release(); + buf.writeByte(0xC0); + + return buf; + } + + @Override + protected Object encodeCommand(Command command) { + + ByteBuf content = Unpooled.buffer(0); + switch (command.getType()) { + case Command.TYPE_POSITION_PERIODIC: + content.writeShort(0x0002); + content.writeShort(6); // length + content.writeShort(command.getInteger(Command.KEY_FREQUENCY)); + return encodeContent(HuaShengProtocolDecoder.MSG_SET_REQ, content); + case Command.TYPE_OUTPUT_CONTROL: + /* +0x01: Lock the relay1; //relay on +0x02: Unlock the relay1; //relay off +0x03: Lock the relay2; //relay2 on +0x04: Unlock the relay2; //relay2 off +0x05: Lock the relay3; //relay3 on +0x06: Unlock the relay3; //realy3 off + */ + content.writeByte( + (command.getInteger(Command.KEY_INDEX) - 1) * 2 + + (2 - command.getInteger(Command.KEY_DATA))); + return encodeContent(HuaShengProtocolDecoder.MSG_CTRL_REQ, content); + case Command.TYPE_ALARM_ARM: + case Command.TYPE_ALARM_DISARM: + content.writeShort(0x0001); + content.writeShort(5); // length + content.writeByte(command.getType().equals(Command.TYPE_ALARM_ARM) ? 1 : 0); + return encodeContent(HuaShengProtocolDecoder.MSG_SET_REQ, content); + case Command.TYPE_SET_SPEED_LIMIT: + content.writeShort(0x0004); + content.writeShort(6); // length + content.writeShort(command.getInteger(Command.KEY_DATA)); + return encodeContent(HuaShengProtocolDecoder.MSG_SET_REQ, content); + default: + return null; + } + } + +} diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocol.java b/src/main/java/org/traccar/protocol/HuabaoProtocol.java index 791672b85..fc12d7d71 100644 --- a/src/main/java/org/traccar/protocol/HuabaoProtocol.java +++ b/src/main/java/org/traccar/protocol/HuabaoProtocol.java @@ -18,17 +18,21 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class HuabaoProtocol extends BaseProtocol { - public HuabaoProtocol() { + @Inject + public HuabaoProtocol(Config config) { setSupportedDataCommands( Command.TYPE_ENGINE_STOP, Command.TYPE_ENGINE_RESUME); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new HuabaoFrameDecoder()); pipeline.addLast(new HuabaoProtocolEncoder(HuabaoProtocol.this)); pipeline.addLast(new HuabaoProtocolDecoder(HuabaoProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java index 0ae08af37..beb1ec41a 100644 --- a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 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. @@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BcdUtil; @@ -51,11 +51,13 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_GENERAL_RESPONSE = 0x8001; public static final int MSG_GENERAL_RESPONSE_2 = 0x4401; public static final int MSG_HEARTBEAT = 0x0002; + public static final int MSG_HEARTBEAT_2 = 0x0506; public static final int MSG_TERMINAL_REGISTER = 0x0100; public static final int MSG_TERMINAL_REGISTER_RESPONSE = 0x8100; public static final int MSG_TERMINAL_CONTROL = 0x8105; public static final int MSG_TERMINAL_AUTH = 0x0102; public static final int MSG_LOCATION_REPORT = 0x0200; + public static final int MSG_LOCATION_BATCH_2 = 0x0210; public static final int MSG_ACCELERATION = 0x2070; public static final int MSG_LOCATION_REPORT_2 = 0x5501; public static final int MSG_LOCATION_REPORT_BLIND = 0x5502; @@ -64,6 +66,7 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_TIME_SYNC_REQUEST = 0x0109; public static final int MSG_TIME_SYNC_RESPONSE = 0x8109; public static final int MSG_PHOTO = 0x8888; + public static final int MSG_TRANSPARENT = 0x0900; public static final int RESULT_SUCCESS = 0; @@ -122,6 +125,9 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { || BitUtil.check(value, 10) || BitUtil.check(value, 11)) { return Position.ALARM_FAULT; } + if (BitUtil.check(value, 7) || BitUtil.check(value, 18)) { + return Position.ALARM_LOW_BATTERY; + } if (BitUtil.check(value, 8)) { return Position.ALARM_POWER_OFF; } @@ -145,6 +151,28 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { return BitUtil.check(value, 15) ? -BitUtil.to(value, 15) : BitUtil.to(value, 15); } + private Date readDate(ByteBuf buf, TimeZone timeZone) { + DateBuilder dateBuilder = new DateBuilder(timeZone) + .setYear(BcdUtil.readInteger(buf, 2)) + .setMonth(BcdUtil.readInteger(buf, 2)) + .setDay(BcdUtil.readInteger(buf, 2)) + .setHour(BcdUtil.readInteger(buf, 2)) + .setMinute(BcdUtil.readInteger(buf, 2)) + .setSecond(BcdUtil.readInteger(buf, 2)); + return dateBuilder.getDate(); + } + + private String decodeId(ByteBuf id) { + String serial = ByteBufUtil.hexDump(id); + if (serial.matches("[0-9]+")) { + return serial; + } else { + long imei = id.getUnsignedShort(0); + imei = (imei << 32) + id.getUnsignedInt(2); + return String.valueOf(imei) + Checksum.luhn(imei); + } + } + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { @@ -178,13 +206,13 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { index = buf.readUnsignedShort(); } - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, ByteBufUtil.hexDump(id)); + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, decodeId(id)); if (deviceSession == null) { return null; } - if (deviceSession.getTimeZone() == null) { - deviceSession.setTimeZone(getTimeZone(deviceSession.getDeviceId(), "GMT+8")); + if (!deviceSession.contains(DeviceSession.KEY_TIMEZONE)) { + deviceSession.set(DeviceSession.KEY_TIMEZONE, getTimeZone(deviceSession.getDeviceId(), "GMT+8")); } if (type == MSG_TERMINAL_REGISTER) { @@ -193,12 +221,12 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { ByteBuf response = Unpooled.buffer(); response.writeShort(index); response.writeByte(RESULT_SUCCESS); - response.writeBytes(ByteBufUtil.hexDump(id).getBytes(StandardCharsets.US_ASCII)); + response.writeBytes(decodeId(id).getBytes(StandardCharsets.US_ASCII)); channel.writeAndFlush(new NetworkMessage( formatMessage(MSG_TERMINAL_REGISTER_RESPONSE, id, false, response), remoteAddress)); } - } else if (type == MSG_TERMINAL_AUTH || type == MSG_HEARTBEAT || type == MSG_PHOTO) { + } else if (type == MSG_TERMINAL_AUTH || type == MSG_HEARTBEAT || type == MSG_HEARTBEAT_2 || type == MSG_PHOTO) { sendGeneralResponse(channel, remoteAddress, id, type, index); @@ -216,11 +244,11 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { return decodeLocation2(deviceSession, buf, type); - } else if (type == MSG_LOCATION_BATCH) { + } else if (type == MSG_LOCATION_BATCH || type == MSG_LOCATION_BATCH_2) { sendGeneralResponse(channel, remoteAddress, id, type, index); - return decodeLocationBatch(deviceSession, buf); + return decodeLocationBatch(deviceSession, buf, type); } else if (type == MSG_TIME_SYNC_REQUEST) { @@ -264,6 +292,12 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { return position; + } else if (type == MSG_TRANSPARENT) { + + sendGeneralResponse(channel, remoteAddress, id, type, index); + + return decodeTransparent(deviceSession, buf); + } return null; @@ -295,6 +329,16 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { case 0x03: position.set(Position.KEY_OBD_SPEED, buf.readUnsignedShort() * 0.1); break; + case 0x56: + buf.readUnsignedByte(); // power level + position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte()); + break; + case 0x61: + position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01); + break; + case 0x69: + position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.01); + break; case 0x80: position.set(Position.KEY_OBD_SPEED, buf.readUnsignedByte()); break; @@ -351,17 +395,13 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { } } - private Position decodeLocation(DeviceSession deviceSession, ByteBuf buf) { - - Position position = new Position(getProtocolName()); - position.setDeviceId(deviceSession.getDeviceId()); - - position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedInt())); + private void decodeCoordinates(Position position, ByteBuf buf) { int status = buf.readInt(); position.set(Position.KEY_IGNITION, BitUtil.check(status, 0)); position.set(Position.KEY_BLOCKED, BitUtil.check(status, 10)); + position.set(Position.KEY_CHARGE, BitUtil.check(status, 26)); position.setValid(BitUtil.check(status, 1)); @@ -379,19 +419,28 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { } else { position.setLongitude(lon); } + } + + private double decodeCustomDouble(ByteBuf buf) { + int b1 = buf.readByte(); + int b2 = buf.readUnsignedByte(); + int sign = b1 != 0 ? b1 / Math.abs(b1) : 1; + return sign * (Math.abs(b1) + b2 / 255.0); + } + + private Position decodeLocation(DeviceSession deviceSession, ByteBuf buf) { + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedInt())); + + decodeCoordinates(position, buf); position.setAltitude(buf.readShort()); position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort() * 0.1)); position.setCourse(buf.readUnsignedShort()); - - DateBuilder dateBuilder = new DateBuilder(deviceSession.getTimeZone()) - .setYear(BcdUtil.readInteger(buf, 2)) - .setMonth(BcdUtil.readInteger(buf, 2)) - .setDay(BcdUtil.readInteger(buf, 2)) - .setHour(BcdUtil.readInteger(buf, 2)) - .setMinute(BcdUtil.readInteger(buf, 2)) - .setSecond(BcdUtil.readInteger(buf, 2)); - position.setTime(dateBuilder.getDate()); + position.setTime(readDate(buf, deviceSession.get(DeviceSession.KEY_TIMEZONE))); if (buf.readableBytes() == 20) { @@ -411,6 +460,7 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { int subtype = buf.readUnsignedByte(); int length = buf.readUnsignedByte(); int endIndex = buf.readerIndex() + length; + String stringValue; switch (subtype) { case 0x01: position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 100); @@ -418,6 +468,9 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { case 0x02: position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedShort() * 0.1); break; + case 0x2b: + position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedInt()); + break; case 0x30: position.set(Position.KEY_RSSI, buf.readUnsignedByte()); break; @@ -425,12 +478,23 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); break; case 0x33: - String sentence = buf.readCharSequence(length, StandardCharsets.US_ASCII).toString(); - if (sentence.startsWith("*M00")) { - String lockStatus = sentence.substring(8, 8 + 7); + stringValue = buf.readCharSequence(length, StandardCharsets.US_ASCII).toString(); + if (stringValue.startsWith("*M00")) { + String lockStatus = stringValue.substring(8, 8 + 7); position.set(Position.KEY_BATTERY, Integer.parseInt(lockStatus.substring(2, 5)) * 0.01); } break; + case 0x56: + position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte() * 10); + buf.readUnsignedByte(); // reserved + break; + case 0x60: + position.set(Position.KEY_EVENT, buf.readUnsignedShort()); + buf.skipBytes(length - 2); + break; + case 0x69: + position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.01); + break; case 0x80: buf.readUnsignedByte(); // content endIndex = buf.writerIndex() - 2; @@ -452,8 +516,8 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { break; case 0x94: if (length > 0) { - position.set( - Position.KEY_VIN, buf.readCharSequence(length, StandardCharsets.US_ASCII).toString()); + stringValue = buf.readCharSequence(length, StandardCharsets.US_ASCII).toString(); + position.set(Position.KEY_VIN, stringValue); } break; case 0xA7: @@ -463,6 +527,14 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { case 0xAC: position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); break; + case 0xBC: + stringValue = buf.readCharSequence(length, StandardCharsets.US_ASCII).toString(); + position.set("driver", stringValue.trim()); + break; + case 0xBD: + stringValue = buf.readCharSequence(length, StandardCharsets.US_ASCII).toString(); + position.set(Position.KEY_DRIVER_UNIQUE_ID, stringValue); + break; case 0xD0: long userStatus = buf.readUnsignedInt(); if (BitUtil.check(userStatus, 3)) { @@ -473,11 +545,22 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.1); break; case 0xD4: - case 0xFE: + case 0xE1: position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte()); break; case 0xD5: - position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.01); + if (length == 2) { + position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.01); + } else { + int count = buf.readUnsignedByte(); + for (int i = 1; i <= count; i++) { + position.set("lock" + i + "Id", ByteBufUtil.hexDump(buf.readSlice(5))); + position.set("lock" + i + "Card", ByteBufUtil.hexDump(buf.readSlice(5))); + position.set("lock" + i + "Battery", buf.readUnsignedByte()); + int status = buf.readUnsignedShort(); + position.set("lock" + i + "Locked", !BitUtil.check(status, 5)); + } + } break; case 0xDA: buf.readUnsignedShort(); // string cut count @@ -486,6 +569,14 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_MOTION, BitUtil.check(deviceStatus, 2)); position.set("cover", BitUtil.check(deviceStatus, 3)); break; + case 0xE6: + while (buf.readerIndex() < endIndex) { + int sensorIndex = buf.readUnsignedByte(); + buf.skipBytes(6); // mac + position.set(Position.PREFIX_TEMP + sensorIndex, decodeCustomDouble(buf)); + position.set("humidity" + sensorIndex, decodeCustomDouble(buf)); + } + break; case 0xEB: if (buf.getUnsignedShort(buf.readerIndex()) > 200) { Network network = new Network(); @@ -513,6 +604,16 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { case 0x00CE: position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01); break; + case 0x00D8: + Network network = new Network(); + network.addCellTower(CellTower.from( + buf.readUnsignedShort(), buf.readUnsignedByte(), + buf.readUnsignedShort(), buf.readUnsignedInt())); + position.setNetwork(network); + break; + case 0xE1: + position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte()); + break; default: buf.skipBytes(extendedLength - 2); break; @@ -521,8 +622,8 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { } break; case 0xED: - String license = buf.readCharSequence(length, StandardCharsets.US_ASCII).toString().trim(); - position.set("driverLicense", license); + stringValue = buf.readCharSequence(length, StandardCharsets.US_ASCII).toString(); + position.set(Position.KEY_CARD, stringValue.trim()); break; case 0xEE: position.set(Position.KEY_RSSI, buf.readUnsignedByte()); @@ -530,6 +631,107 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001); position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); break; + case 0xF3: + while (buf.readerIndex() < endIndex) { + int extendedType = buf.readUnsignedShort(); + int extendedLength = buf.readUnsignedByte(); + switch (extendedType) { + case 0x0002: + position.set(Position.KEY_OBD_SPEED, buf.readUnsignedShort() * 0.1); + break; + case 0x0003: + position.set(Position.KEY_RPM, buf.readUnsignedShort()); + break; + case 0x0004: + position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.001); + break; + case 0x0005: + position.set(Position.KEY_OBD_ODOMETER, buf.readUnsignedInt() * 100); + break; + case 0x0007: + position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedShort() * 0.1); + break; + case 0x0008: + position.set(Position.KEY_ENGINE_LOAD, buf.readUnsignedShort() * 0.1); + break; + case 0x0009: + position.set(Position.KEY_COOLANT_TEMP, buf.readUnsignedShort() - 40); + break; + case 0x000B: + position.set("intakePressure", buf.readUnsignedShort()); + break; + case 0x000C: + position.set("intakeTemp", buf.readUnsignedShort() - 40); + break; + case 0x000D: + position.set("intakeFlow", buf.readUnsignedShort()); + break; + case 0x000E: + position.set(Position.KEY_THROTTLE, buf.readUnsignedShort() * 100 / 255); + break; + case 0x0050: + position.set(Position.KEY_VIN, buf.readSlice(17).toString(StandardCharsets.US_ASCII)); + break; + case 0x0100: + position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedShort() * 0.1); + break; + case 0x0102: + position.set("tripFuel", buf.readUnsignedShort() * 0.1); + break; + case 0x0112: + position.set("hardAccelerationCount", buf.readUnsignedShort()); + break; + case 0x0113: + position.set("hardDecelerationCount", buf.readUnsignedShort()); + break; + case 0x0114: + position.set("hardCorneringCount", buf.readUnsignedShort()); + break; + default: + buf.skipBytes(extendedLength); + break; + } + } + break; + case 0xFE: + if (length == 1) { + position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte()); + } else if (length == 2) { + position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.1); + } else { + int mark = buf.readUnsignedByte(); + if (mark == 0x7C) { + while (buf.readerIndex() < endIndex) { + int extendedType = buf.readUnsignedByte(); + int extendedLength = buf.readUnsignedByte(); + switch (extendedType) { + case 0x01: + long alarms = buf.readUnsignedInt(); + if (BitUtil.check(alarms, 0)) { + position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION); + } + if (BitUtil.check(alarms, 1)) { + position.set(Position.KEY_ALARM, Position.ALARM_BRAKING); + } + if (BitUtil.check(alarms, 2)) { + position.set(Position.KEY_ALARM, Position.ALARM_CORNERING); + } + if (BitUtil.check(alarms, 3)) { + position.set(Position.KEY_ALARM, Position.ALARM_ACCIDENT); + } + if (BitUtil.check(alarms, 4)) { + position.set(Position.KEY_ALARM, Position.ALARM_TAMPERING); + } + break; + default: + buf.skipBytes(extendedLength); + break; + } + } + } + position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte()); + } + break; default: break; } @@ -558,7 +760,8 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_CHARGE, true); } - position.setNetwork(new Network(CellTower.fromCidLac(buf.readUnsignedInt(), buf.readUnsignedShort()))); + position.setNetwork(new Network(CellTower.fromCidLac( + getConfig(), buf.readUnsignedInt(), buf.readUnsignedShort()))); int product = buf.readUnsignedByte(); int status = buf.readUnsignedShort(); @@ -583,27 +786,235 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_STATUS, status); + while (buf.readableBytes() > 2) { + int id = buf.readUnsignedByte(); + int length = buf.readUnsignedByte(); + switch (id) { + case 0x02: + position.setAltitude(buf.readShort()); + break; + case 0x0C: + int x = buf.readUnsignedShort(); + if (x > 0x8000) { + x -= 0x10000; + } + int y = buf.readUnsignedShort(); + if (y > 0x8000) { + y -= 0x10000; + } + int z = buf.readUnsignedShort(); + if (z > 0x8000) { + z -= 0x10000; + } + position.set("tilt", String.format("[%d,%d,%d]", x, y, z)); + break; + default: + buf.skipBytes(length); + break; + } + } + return position; } - private List<Position> decodeLocationBatch(DeviceSession deviceSession, ByteBuf buf) { + private List<Position> decodeLocationBatch(DeviceSession deviceSession, ByteBuf buf, int type) { List<Position> positions = new LinkedList<>(); - int count = buf.readUnsignedShort(); - int locationType = buf.readUnsignedByte(); + int locationType = 0; + if (type == MSG_LOCATION_BATCH) { + buf.readUnsignedShort(); // count + locationType = buf.readUnsignedByte(); + } - for (int i = 0; i < count; i++) { - int endIndex = buf.readUnsignedShort() + buf.readerIndex(); - Position position = decodeLocation(deviceSession, buf); + while (buf.readableBytes() > 2) { + int length = type == MSG_LOCATION_BATCH_2 ? buf.readUnsignedByte() : buf.readUnsignedShort(); + ByteBuf fragment = buf.readSlice(length); + Position position = decodeLocation(deviceSession, fragment); if (locationType > 0) { position.set(Position.KEY_ARCHIVE, true); } positions.add(position); - buf.readerIndex(endIndex); } return positions; } + private Position decodeTransparent(DeviceSession deviceSession, ByteBuf buf) { + + int type = buf.readUnsignedByte(); + + if (type == 0xF0) { + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + Date time = readDate(buf, deviceSession.get(DeviceSession.KEY_TIMEZONE)); + + if (buf.readUnsignedByte() > 0) { + position.set(Position.KEY_ARCHIVE, true); + } + + buf.readUnsignedByte(); // vehicle type + + int count; + int subtype = buf.readUnsignedByte(); + switch (subtype) { + case 0x01: + count = buf.readUnsignedByte(); + for (int i = 0; i < count; i++) { + int id = buf.readUnsignedShort(); + int length = buf.readUnsignedByte(); + switch (id) { + case 0x0102: + case 0x0528: + case 0x0546: + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 100); + break; + case 0x0103: + position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedInt() * 0.01); + break; + case 0x052A: + position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedShort() * 0.01); + break; + case 0x0105: + case 0x052C: + position.set(Position.KEY_FUEL_USED, buf.readUnsignedInt() * 0.01); + break; + case 0x014A: + case 0x0537: + case 0x0538: + case 0x0539: + position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedShort() * 0.01); + break; + case 0x052B: + position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedByte()); + break; + case 0x052D: + position.set(Position.KEY_COOLANT_TEMP, buf.readUnsignedByte() - 40); + break; + case 0x052E: + position.set("airTemp", buf.readUnsignedByte() - 40); + break; + case 0x0530: + position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.001); + break; + case 0x0535: + position.set(Position.KEY_OBD_SPEED, buf.readUnsignedShort() * 0.1); + break; + case 0x0536: + position.set(Position.KEY_RPM, buf.readUnsignedShort()); + break; + case 0x053D: + position.set("intakePressure", buf.readUnsignedShort() * 0.1); + break; + case 0x0544: + position.set("liquidLevel", buf.readUnsignedByte()); + break; + case 0x0547: + case 0x0548: + position.set(Position.KEY_THROTTLE, buf.readUnsignedByte()); + break; + default: + switch (length) { + case 1: + position.set(Position.PREFIX_IO + id, buf.readUnsignedByte()); + break; + case 2: + position.set(Position.PREFIX_IO + id, buf.readUnsignedShort()); + break; + case 4: + position.set(Position.PREFIX_IO + id, buf.readUnsignedInt()); + break; + default: + buf.skipBytes(length); + break; + } + break; + } + } + getLastLocation(position, time); + decodeCoordinates(position, buf); + position.setTime(time); + break; + case 0x02: + List<String> codes = new LinkedList<>(); + count = buf.readUnsignedShort(); + for (int i = 0; i < count; i++) { + buf.readUnsignedInt(); // system id + int codeCount = buf.readUnsignedShort(); + for (int j = 0; j < codeCount; j++) { + buf.readUnsignedInt(); // dtc + buf.readUnsignedInt(); // status + codes.add(buf.readCharSequence( + buf.readUnsignedShort(), StandardCharsets.US_ASCII).toString().trim()); + } + } + position.set(Position.KEY_DTCS, String.join(" ", codes)); + getLastLocation(position, time); + decodeCoordinates(position, buf); + position.setTime(time); + break; + case 0x03: + count = buf.readUnsignedByte(); + for (int i = 0; i < count; i++) { + int id = buf.readUnsignedByte(); + int length = buf.readUnsignedByte(); + switch (id) { + case 0x01: + position.set(Position.KEY_ALARM, Position.ALARM_POWER_RESTORED); + break; + case 0x02: + position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT); + break; + case 0x1A: + position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION); + break; + case 0x1B: + position.set(Position.KEY_ALARM, Position.ALARM_BRAKING); + break; + case 0x1C: + position.set(Position.KEY_ALARM, Position.ALARM_CORNERING); + break; + case 0x1D: + case 0x1E: + case 0x1F: + position.set(Position.KEY_ALARM, Position.ALARM_LANE_CHANGE); + break; + case 0x23: + position.set(Position.KEY_ALARM, Position.ALARM_FATIGUE_DRIVING); + break; + case 0x26: + case 0x27: + case 0x28: + position.set(Position.KEY_ALARM, Position.ALARM_ACCIDENT); + break; + case 0x31: + case 0x32: + position.set(Position.KEY_ALARM, Position.ALARM_DOOR); + break; + default: + break; + } + buf.skipBytes(length); + } + getLastLocation(position, time); + decodeCoordinates(position, buf); + position.setTime(time); + break; + case 0x0B: + if (buf.readUnsignedByte() > 0) { + position.set(Position.KEY_VIN, buf.readCharSequence(17, StandardCharsets.US_ASCII).toString()); + } + getLastLocation(position, time); + break; + default: + return null; + } + + return position; + } + + return null; + } + } diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocolEncoder.java b/src/main/java/org/traccar/protocol/HuabaoProtocolEncoder.java index 55c1e0c3b..ada7e3fba 100644 --- a/src/main/java/org/traccar/protocol/HuabaoProtocolEncoder.java +++ b/src/main/java/org/traccar/protocol/HuabaoProtocolEncoder.java @@ -18,10 +18,11 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import org.traccar.BaseProtocolEncoder; -import org.traccar.Context; +import org.traccar.Protocol; +import org.traccar.config.Keys; import org.traccar.helper.DataConverter; +import org.traccar.helper.model.AttributeUtil; import org.traccar.model.Command; -import org.traccar.Protocol; import java.text.SimpleDateFormat; import java.util.Date; @@ -35,8 +36,8 @@ public class HuabaoProtocolEncoder extends BaseProtocolEncoder { @Override protected Object encodeCommand(Command command) { - boolean alternative = Context.getIdentityManager().lookupAttributeBoolean( - command.getDeviceId(), getProtocolName() + ".alternative", false, false, true); + boolean alternative = AttributeUtil.lookup( + getCacheManager(), Keys.PROTOCOL_ALTERNATIVE.withPrefix(getProtocolName()), command.getDeviceId()); ByteBuf id = Unpooled.wrappedBuffer( DataConverter.parseHex(getUniqueId(command.getDeviceId()))); diff --git a/src/main/java/org/traccar/protocol/HunterProProtocol.java b/src/main/java/org/traccar/protocol/HunterProProtocol.java index 9f6424a57..64dab33b1 100644 --- a/src/main/java/org/traccar/protocol/HunterProProtocol.java +++ b/src/main/java/org/traccar/protocol/HunterProProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class HunterProProtocol extends BaseProtocol { - public HunterProProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public HunterProProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "\r")); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/HunterProProtocolDecoder.java b/src/main/java/org/traccar/protocol/HunterProProtocolDecoder.java index 06bc12d59..eada1fd9a 100644 --- a/src/main/java/org/traccar/protocol/HunterProProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/HunterProProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/IdplProtocol.java b/src/main/java/org/traccar/protocol/IdplProtocol.java index 418178756..1e44ad74c 100644 --- a/src/main/java/org/traccar/protocol/IdplProtocol.java +++ b/src/main/java/org/traccar/protocol/IdplProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class IdplProtocol extends BaseProtocol { - public IdplProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public IdplProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/IdplProtocolDecoder.java b/src/main/java/org/traccar/protocol/IdplProtocolDecoder.java index cf3c03d7f..72409b168 100644 --- a/src/main/java/org/traccar/protocol/IdplProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/IdplProtocolDecoder.java @@ -20,7 +20,7 @@ import java.util.regex.Pattern; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.Parser.CoordinateFormat; diff --git a/src/main/java/org/traccar/protocol/IntellitracProtocol.java b/src/main/java/org/traccar/protocol/IntellitracProtocol.java index 3abf40da7..a82e6a5db 100644 --- a/src/main/java/org/traccar/protocol/IntellitracProtocol.java +++ b/src/main/java/org/traccar/protocol/IntellitracProtocol.java @@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class IntellitracProtocol extends BaseProtocol { - public IntellitracProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public IntellitracProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new IntellitracFrameDecoder(1024)); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/IntellitracProtocolDecoder.java b/src/main/java/org/traccar/protocol/IntellitracProtocolDecoder.java index 930d4f23b..b86584016 100644 --- a/src/main/java/org/traccar/protocol/IntellitracProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/IntellitracProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/IotmProtocol.java b/src/main/java/org/traccar/protocol/IotmProtocol.java index f202d9b9d..1631b67d8 100644 --- a/src/main/java/org/traccar/protocol/IotmProtocol.java +++ b/src/main/java/org/traccar/protocol/IotmProtocol.java @@ -20,13 +20,17 @@ import io.netty.handler.codec.mqtt.MqttEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class IotmProtocol extends BaseProtocol { - public IotmProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public IotmProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(MqttEncoder.INSTANCE); pipeline.addLast(new MqttDecoder()); pipeline.addLast(new IotmProtocolDecoder(IotmProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/IotmProtocolDecoder.java b/src/main/java/org/traccar/protocol/IotmProtocolDecoder.java index 9c94ffd4b..7bbe6c8de 100644 --- a/src/main/java/org/traccar/protocol/IotmProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/IotmProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2020 - 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. @@ -25,7 +25,7 @@ import io.netty.handler.codec.mqtt.MqttMessageBuilders; import io.netty.handler.codec.mqtt.MqttPublishMessage; import io.netty.handler.codec.mqtt.MqttSubscribeMessage; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.UnitsConverter; @@ -260,7 +260,7 @@ public class IotmProtocolDecoder extends BaseProtocolDecoder { MqttSubscribeMessage message = (MqttSubscribeMessage) msg; MqttMessage response = MqttMessageBuilders.subAck() - .packetId((short) message.variableHeader().messageId()) + .packetId(message.variableHeader().messageId()) .build(); if (channel != null) { @@ -339,7 +339,7 @@ public class IotmProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedByte(); // checksum MqttMessage response = MqttMessageBuilders.pubAck() - .packetId((short) message.variableHeader().packetId()) + .packetId(message.variableHeader().packetId()) .build(); if (channel != null) { diff --git a/src/main/java/org/traccar/protocol/ItsProtocol.java b/src/main/java/org/traccar/protocol/ItsProtocol.java index 45df3da11..7d59ea60c 100644 --- a/src/main/java/org/traccar/protocol/ItsProtocol.java +++ b/src/main/java/org/traccar/protocol/ItsProtocol.java @@ -20,17 +20,21 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class ItsProtocol extends BaseProtocol { - public ItsProtocol() { + @Inject + public ItsProtocol(Config config) { setSupportedDataCommands( Command.TYPE_ENGINE_STOP, Command.TYPE_ENGINE_RESUME); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new ItsFrameDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java b/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java index 9eed58347..8a8d734cf 100644 --- a/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.Parser; @@ -205,7 +205,8 @@ public class ItsProtocolDecoder extends BaseProtocolDecoder { if (parser.hasNext()) { position.setValid(parser.nextInt() == 1); } - position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS)); + position.setTime(parser.nextDateTime( + Parser.DateTimeFormat.DMY_HMS, getTimeZone(deviceSession.getDeviceId()).getID())); if (parser.hasNext()) { position.setValid(parser.next().matches("[1A]")); } diff --git a/src/main/java/org/traccar/protocol/Ivt401Protocol.java b/src/main/java/org/traccar/protocol/Ivt401Protocol.java index fb44e4fe9..5132c7467 100644 --- a/src/main/java/org/traccar/protocol/Ivt401Protocol.java +++ b/src/main/java/org/traccar/protocol/Ivt401Protocol.java @@ -20,13 +20,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class Ivt401Protocol extends BaseProtocol { - public Ivt401Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public Ivt401Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ';')); pipeline.addLast(new StringDecoder()); pipeline.addLast(new Ivt401ProtocolDecoder(Ivt401Protocol.this)); diff --git a/src/main/java/org/traccar/protocol/Ivt401ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Ivt401ProtocolDecoder.java index 63556e7a9..972f22ebe 100644 --- a/src/main/java/org/traccar/protocol/Ivt401ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Ivt401ProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/JidoProtocol.java b/src/main/java/org/traccar/protocol/JidoProtocol.java index 2a2e71dbe..b30cc586a 100644 --- a/src/main/java/org/traccar/protocol/JidoProtocol.java +++ b/src/main/java/org/traccar/protocol/JidoProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class JidoProtocol extends BaseProtocol { - public JidoProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public JidoProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '#')); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/JidoProtocolDecoder.java b/src/main/java/org/traccar/protocol/JidoProtocolDecoder.java index 40fa8864d..98fb36e11 100644 --- a/src/main/java/org/traccar/protocol/JidoProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/JidoProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/JpKorjarProtocol.java b/src/main/java/org/traccar/protocol/JpKorjarProtocol.java index fe5b2480d..ae312ea3e 100644 --- a/src/main/java/org/traccar/protocol/JpKorjarProtocol.java +++ b/src/main/java/org/traccar/protocol/JpKorjarProtocol.java @@ -1,6 +1,6 @@ /* + * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org) * Copyright 2016 Nyash (nyashh@gmail.com) - * Copyright 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. @@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class JpKorjarProtocol extends BaseProtocol { - public JpKorjarProtocol() { - addServer(new TrackerServer(false, this.getName()) { + @Inject + public JpKorjarProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new JpKorjarFrameDecoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new JpKorjarProtocolDecoder(JpKorjarProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/JpKorjarProtocolDecoder.java b/src/main/java/org/traccar/protocol/JpKorjarProtocolDecoder.java index 33026918a..ffddcc568 100644 --- a/src/main/java/org/traccar/protocol/JpKorjarProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/JpKorjarProtocolDecoder.java @@ -18,7 +18,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/Jt600FrameDecoder.java b/src/main/java/org/traccar/protocol/Jt600FrameDecoder.java index bfefb94a7..f7890f814 100644 --- a/src/main/java/org/traccar/protocol/Jt600FrameDecoder.java +++ b/src/main/java/org/traccar/protocol/Jt600FrameDecoder.java @@ -35,7 +35,7 @@ public class Jt600FrameDecoder extends BaseFrameDecoder { char type = (char) buf.getByte(buf.readerIndex()); if (type == '$') { - boolean longFormat = Jt600ProtocolDecoder.isLongFormat(buf, buf.readerIndex() + 1); + boolean longFormat = Jt600ProtocolDecoder.isLongFormat(buf); int length = buf.getUnsignedShort(buf.readerIndex() + (longFormat ? 8 : 7)) + 10; if (length <= buf.readableBytes()) { return buf.readRetainedSlice(length); diff --git a/src/main/java/org/traccar/protocol/Jt600Protocol.java b/src/main/java/org/traccar/protocol/Jt600Protocol.java index 37c82f741..9dc62662f 100644 --- a/src/main/java/org/traccar/protocol/Jt600Protocol.java +++ b/src/main/java/org/traccar/protocol/Jt600Protocol.java @@ -19,19 +19,23 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class Jt600Protocol extends BaseProtocol { - public Jt600Protocol() { + @Inject + public Jt600Protocol(Config config) { setSupportedDataCommands( Command.TYPE_ENGINE_RESUME, Command.TYPE_ENGINE_STOP, Command.TYPE_SET_TIMEZONE, Command.TYPE_REBOOT_DEVICE); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new Jt600FrameDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new Jt600ProtocolEncoder(Jt600Protocol.this)); diff --git a/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java index b4b70091b..dc763dea7 100644 --- a/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BcdUtil; @@ -86,8 +86,8 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder { } - static boolean isLongFormat(ByteBuf buf, int flagIndex) { - return buf.getUnsignedByte(flagIndex) >> 4 >= 7; + static boolean isLongFormat(ByteBuf buf) { + return buf.getUnsignedByte(buf.readerIndex() + 8) == 0; } static void decodeBinaryLocation(ByteBuf buf, Position position) { @@ -123,9 +123,9 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder { List<Position> positions = new LinkedList<>(); - buf.readByte(); // header + boolean longFormat = isLongFormat(buf); - boolean longFormat = isLongFormat(buf, buf.readerIndex()); + buf.readByte(); // header String id = String.valueOf(Long.parseLong(ByteBufUtil.hexDump(buf.readSlice(5)))); DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id); @@ -143,7 +143,7 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder { boolean responseRequired = false; - while (buf.readableBytes() > 1) { + while (buf.readableBytes() >= 17) { Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); @@ -177,7 +177,8 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_BATTERY_LEVEL, battery); } - CellTower cellTower = CellTower.fromCidLac(buf.readUnsignedShort(), buf.readUnsignedShort()); + CellTower cellTower = CellTower.fromCidLac( + getConfig(), buf.readUnsignedShort(), buf.readUnsignedShort()); cellTower.setSignalStrength((int) buf.readUnsignedByte()); position.setNetwork(new Network(cellTower)); @@ -201,7 +202,7 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder { int rssi = buf.readUnsignedByte(); if (cid != 0 && lac != 0) { - CellTower cellTower = CellTower.fromCidLac(cid, lac); + CellTower cellTower = CellTower.fromCidLac(getConfig(), cid, lac); cellTower.setSignalStrength(rssi); position.setNetwork(new Network(cellTower)); } else { @@ -356,7 +357,7 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt(0)); position.set(Position.KEY_STATUS, parser.nextBinInt(0)); - CellTower cellTower = CellTower.fromCidLac(parser.nextInt(0), parser.nextInt(0)); + CellTower cellTower = CellTower.fromCidLac(getConfig(), parser.nextInt(0), parser.nextInt(0)); cellTower.setSignalStrength(parser.nextInt(0)); position.setNetwork(new Network(cellTower)); diff --git a/src/main/java/org/traccar/protocol/KenjiProtocol.java b/src/main/java/org/traccar/protocol/KenjiProtocol.java index 90c0c511c..b4e610cbd 100644 --- a/src/main/java/org/traccar/protocol/KenjiProtocol.java +++ b/src/main/java/org/traccar/protocol/KenjiProtocol.java @@ -22,13 +22,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class KenjiProtocol extends BaseProtocol { - public KenjiProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public KenjiProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/KenjiProtocolDecoder.java b/src/main/java/org/traccar/protocol/KenjiProtocolDecoder.java index 63812242a..fb989c72e 100644 --- a/src/main/java/org/traccar/protocol/KenjiProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/KenjiProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.BitUtil; import org.traccar.helper.DateBuilder; diff --git a/src/main/java/org/traccar/protocol/KhdProtocol.java b/src/main/java/org/traccar/protocol/KhdProtocol.java index 60a2aea7f..add13ef16 100644 --- a/src/main/java/org/traccar/protocol/KhdProtocol.java +++ b/src/main/java/org/traccar/protocol/KhdProtocol.java @@ -19,11 +19,15 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class KhdProtocol extends BaseProtocol { - public KhdProtocol() { + @Inject + public KhdProtocol(Config config) { setSupportedDataCommands( Command.TYPE_ENGINE_STOP, Command.TYPE_ENGINE_RESUME, @@ -33,9 +37,9 @@ public class KhdProtocol extends BaseProtocol { Command.TYPE_SET_ODOMETER, Command.TYPE_POSITION_SINGLE); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(512, 3, 2)); pipeline.addLast(new KhdProtocolEncoder(KhdProtocol.this)); pipeline.addLast(new KhdProtocolDecoder(KhdProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java b/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java index a14f9b8a4..dd2e1dbfd 100644 --- a/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java @@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BcdUtil; @@ -205,6 +205,9 @@ public class KhdProtocolDecoder extends BaseProtocolDecoder { } } break; + case 0x20: + position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte()); + break; case 0x23: Network network = new Network(); int count = buf.readUnsignedByte(); diff --git a/src/main/java/org/traccar/protocol/KhdProtocolEncoder.java b/src/main/java/org/traccar/protocol/KhdProtocolEncoder.java index 8aeb9660d..12353b415 100644 --- a/src/main/java/org/traccar/protocol/KhdProtocolEncoder.java +++ b/src/main/java/org/traccar/protocol/KhdProtocolEncoder.java @@ -84,7 +84,7 @@ public class KhdProtocolEncoder extends BaseProtocolEncoder { return encodeCommand(MSG_FACTORY_RESET, uniqueId, null); case Command.TYPE_SET_SPEED_LIMIT: ByteBuf content = Unpooled.buffer(); - content.writeByte(Integer.parseInt(command.getString(Command.KEY_DATA))); + content.writeByte(command.getInteger(Command.KEY_DATA)); return encodeCommand(MSG_RESUME_OIL, uniqueId, content); case Command.TYPE_SET_ODOMETER: return encodeCommand(MSG_DELETE_MILEAGE, uniqueId, null); diff --git a/src/main/java/org/traccar/protocol/L100Protocol.java b/src/main/java/org/traccar/protocol/L100Protocol.java index 942029307..fa6d1b07e 100644 --- a/src/main/java/org/traccar/protocol/L100Protocol.java +++ b/src/main/java/org/traccar/protocol/L100Protocol.java @@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class L100Protocol extends BaseProtocol { - public L100Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public L100Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new L100FrameDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/L100ProtocolDecoder.java b/src/main/java/org/traccar/protocol/L100ProtocolDecoder.java index 5b5eb7d60..820de8f1c 100644 --- a/src/main/java/org/traccar/protocol/L100ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/L100ProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.Checksum; diff --git a/src/main/java/org/traccar/protocol/LacakProtocol.java b/src/main/java/org/traccar/protocol/LacakProtocol.java index 0a0499ad7..ddaf5078d 100644 --- a/src/main/java/org/traccar/protocol/LacakProtocol.java +++ b/src/main/java/org/traccar/protocol/LacakProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class LacakProtocol extends BaseProtocol { - public LacakProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public LacakProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new HttpResponseEncoder()); pipeline.addLast(new HttpRequestDecoder()); pipeline.addLast(new HttpObjectAggregator(16384)); diff --git a/src/main/java/org/traccar/protocol/LacakProtocolDecoder.java b/src/main/java/org/traccar/protocol/LacakProtocolDecoder.java index 132087c8f..66aab3490 100644 --- a/src/main/java/org/traccar/protocol/LacakProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/LacakProtocolDecoder.java @@ -19,13 +19,13 @@ import io.netty.channel.Channel; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpResponseStatus; import org.traccar.BaseHttpProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.DateUtil; import org.traccar.model.Position; -import javax.json.Json; -import javax.json.JsonObject; +import jakarta.json.Json; +import jakarta.json.JsonObject; import java.io.StringReader; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; diff --git a/src/main/java/org/traccar/protocol/LaipacProtocol.java b/src/main/java/org/traccar/protocol/LaipacProtocol.java index 1d561dbd2..65b1a57e9 100644 --- a/src/main/java/org/traccar/protocol/LaipacProtocol.java +++ b/src/main/java/org/traccar/protocol/LaipacProtocol.java @@ -18,23 +18,27 @@ package org.traccar.protocol; import io.netty.handler.codec.LineBasedFrameDecoder; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; -import org.traccar.model.Command; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; +import org.traccar.model.Command; + +import jakarta.inject.Inject; public class LaipacProtocol extends BaseProtocol { - public LaipacProtocol() { + @Inject + public LaipacProtocol(Config config) { setSupportedDataCommands( Command.TYPE_CUSTOM, Command.TYPE_POSITION_SINGLE, Command.TYPE_REBOOT_DEVICE); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java b/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java index 45890e9a2..5745909c7 100644 --- a/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java @@ -17,8 +17,8 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; +import org.traccar.helper.model.AttributeUtil; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.Checksum; @@ -28,6 +28,8 @@ import org.traccar.helper.PatternBuilder; import org.traccar.model.CellTower; import org.traccar.model.Network; import org.traccar.model.Position; +import org.traccar.model.Device; +import org.traccar.helper.BitUtil; import java.net.SocketAddress; import java.util.regex.Pattern; @@ -108,19 +110,31 @@ public class LaipacProtocolDecoder extends BaseProtocolDecoder { } } - private String decodeEvent(String event, Position position) { + private String decodeEvent(String event, Position position, String model) { if (event.length() == 1) { char inputStatus = event.charAt(0); if (inputStatus >= 'A' && inputStatus <= 'D') { int inputStatusInt = inputStatus - 'A'; - position.set(Position.PREFIX_IN + 1, inputStatusInt & 1); - position.set(Position.PREFIX_IN + 2, inputStatusInt & 2); + position.set(Position.PREFIX_IN + 1, (boolean) BitUtil.check(inputStatusInt, 0)); + position.set(Position.PREFIX_IN + 2, (boolean) BitUtil.check(inputStatusInt, 1)); + if ("SF-Lite".equals(model)) { + position.set(Position.PREFIX_IN + 3, false); + } + return null; + } else if (inputStatus >= 'O' && inputStatus <= 'R') { + int inputStatusInt = inputStatus - 'O'; + position.set(Position.PREFIX_IN + 1, (boolean) BitUtil.check(inputStatusInt, 0)); + position.set(Position.PREFIX_IN + 2, (boolean) BitUtil.check(inputStatusInt, 1)); + if ("SF-Lite".equals(model)) { + position.set(Position.PREFIX_IN + 3, true); + } return null; } } return event; + } private void sendEventResponse( @@ -132,6 +146,9 @@ public class LaipacProtocolDecoder extends BaseProtocolDecoder { case "3": responseCode = "d"; break; + case "M": + responseCode = "m"; + break; case "S": case "T": responseCode = "t"; @@ -209,6 +226,8 @@ public class LaipacProtocolDecoder extends BaseProtocolDecoder { return null; } + String model = getCacheManager().getObject(Device.class, deviceSession.getDeviceId()).getModel(); + Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); @@ -230,12 +249,17 @@ public class LaipacProtocolDecoder extends BaseProtocolDecoder { String event = parser.next(); position.set(Position.KEY_ALARM, decodeAlarm(event)); - position.set(Position.KEY_EVENT, decodeEvent(event, position)); + position.set(Position.KEY_EVENT, decodeEvent(event, position, model)); position.set(Position.KEY_BATTERY, Double.parseDouble(parser.next().replaceAll("\\.", "")) * 0.001); position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000); position.set(Position.KEY_GPS, parser.nextInt()); position.set(Position.PREFIX_ADC + 1, parser.nextDouble() * 0.001); - position.set(Position.PREFIX_ADC + 2, parser.nextDouble() * 0.001); + + if ("AVL110".equals(model) || "AVL120".equals(model)) { + position.set(Position.PREFIX_ADC + 2, parser.nextDouble() * 0.001); + } else { + parser.next(); + } Integer lac = parser.nextHexInt(); Integer cid = parser.nextHexInt(); @@ -253,8 +277,8 @@ public class LaipacProtocolDecoder extends BaseProtocolDecoder { sendAcknowledge(status, event, checksum, channel, remoteAddress); - String devicePassword = Context.getIdentityManager() - .getDevicePassword(deviceSession.getDeviceId(), getProtocolName(), DEFAULT_DEVICE_PASSWORD); + String devicePassword = AttributeUtil.getDevicePassword( + getCacheManager(), deviceSession.getDeviceId(), getProtocolName(), DEFAULT_DEVICE_PASSWORD); sendEventResponse(event, devicePassword, channel, remoteAddress); } diff --git a/src/main/java/org/traccar/protocol/LeafSpyProtocol.java b/src/main/java/org/traccar/protocol/LeafSpyProtocol.java index 05f63a2d7..9e167e7ba 100644 --- a/src/main/java/org/traccar/protocol/LeafSpyProtocol.java +++ b/src/main/java/org/traccar/protocol/LeafSpyProtocol.java @@ -22,13 +22,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class LeafSpyProtocol extends BaseProtocol { - public LeafSpyProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public LeafSpyProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new HttpResponseEncoder()); pipeline.addLast(new HttpRequestDecoder()); pipeline.addLast(new HttpObjectAggregator(16384)); diff --git a/src/main/java/org/traccar/protocol/LeafSpyProtocolDecoder.java b/src/main/java/org/traccar/protocol/LeafSpyProtocolDecoder.java index ad0c9bd32..6affb85c5 100644 --- a/src/main/java/org/traccar/protocol/LeafSpyProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/LeafSpyProtocolDecoder.java @@ -22,7 +22,7 @@ import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.QueryStringDecoder; import org.traccar.BaseHttpProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.model.Position; diff --git a/src/main/java/org/traccar/protocol/M2cProtocol.java b/src/main/java/org/traccar/protocol/M2cProtocol.java index 9de8526c3..8abc30f60 100644 --- a/src/main/java/org/traccar/protocol/M2cProtocol.java +++ b/src/main/java/org/traccar/protocol/M2cProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class M2cProtocol extends BaseProtocol { - public M2cProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public M2cProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(32 * 1024, ']')); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/M2cProtocolDecoder.java b/src/main/java/org/traccar/protocol/M2cProtocolDecoder.java index 1460bb176..9415d0f07 100644 --- a/src/main/java/org/traccar/protocol/M2cProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/M2cProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/M2mProtocol.java b/src/main/java/org/traccar/protocol/M2mProtocol.java index dda328a59..03a069d66 100644 --- a/src/main/java/org/traccar/protocol/M2mProtocol.java +++ b/src/main/java/org/traccar/protocol/M2mProtocol.java @@ -19,13 +19,17 @@ import io.netty.handler.codec.FixedLengthFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class M2mProtocol extends BaseProtocol { - public M2mProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public M2mProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new FixedLengthFrameDecoder(23)); pipeline.addLast(new M2mProtocolDecoder(M2mProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/M2mProtocolDecoder.java b/src/main/java/org/traccar/protocol/M2mProtocolDecoder.java index 21e4a2fd0..7eca93a59 100644 --- a/src/main/java/org/traccar/protocol/M2mProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/M2mProtocolDecoder.java @@ -18,7 +18,7 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; import org.traccar.model.Position; diff --git a/src/main/java/org/traccar/protocol/MaestroProtocol.java b/src/main/java/org/traccar/protocol/MaestroProtocol.java index 87453ce7d..29f0b8897 100644 --- a/src/main/java/org/traccar/protocol/MaestroProtocol.java +++ b/src/main/java/org/traccar/protocol/MaestroProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class MaestroProtocol extends BaseProtocol { - public MaestroProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public MaestroProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new FixedLengthFrameDecoder(160)); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/MaestroProtocolDecoder.java b/src/main/java/org/traccar/protocol/MaestroProtocolDecoder.java index 37b097414..78308658e 100644 --- a/src/main/java/org/traccar/protocol/MaestroProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/MaestroProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/ManPowerProtocol.java b/src/main/java/org/traccar/protocol/ManPowerProtocol.java index 49d8b1e9f..ba2414ca7 100644 --- a/src/main/java/org/traccar/protocol/ManPowerProtocol.java +++ b/src/main/java/org/traccar/protocol/ManPowerProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class ManPowerProtocol extends BaseProtocol { - public ManPowerProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public ManPowerProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ';')); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/ManPowerProtocolDecoder.java b/src/main/java/org/traccar/protocol/ManPowerProtocolDecoder.java index 2c7b7eb40..8ac13b4d4 100644 --- a/src/main/java/org/traccar/protocol/ManPowerProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/ManPowerProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/Mavlink2Protocol.java b/src/main/java/org/traccar/protocol/Mavlink2Protocol.java index d779648e4..916fb7467 100644 --- a/src/main/java/org/traccar/protocol/Mavlink2Protocol.java +++ b/src/main/java/org/traccar/protocol/Mavlink2Protocol.java @@ -15,18 +15,21 @@ */ package org.traccar.protocol; +import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; -import io.netty.handler.codec.LengthFieldBasedFrameDecoder; +import jakarta.inject.Inject; public class Mavlink2Protocol extends BaseProtocol { - public Mavlink2Protocol() { - addServer(new TrackerServer(true, getName()) { + @Inject + public Mavlink2Protocol(Config config) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 1, 1, 10, 0)); pipeline.addLast(new Mavlink2ProtocolDecoder(Mavlink2Protocol.this)); } diff --git a/src/main/java/org/traccar/protocol/Mavlink2ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Mavlink2ProtocolDecoder.java index 431258388..fac930ba8 100644 --- a/src/main/java/org/traccar/protocol/Mavlink2ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Mavlink2ProtocolDecoder.java @@ -18,7 +18,7 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; diff --git a/src/main/java/org/traccar/protocol/MegastekProtocol.java b/src/main/java/org/traccar/protocol/MegastekProtocol.java index e9f5f9fde..9f8937f01 100644 --- a/src/main/java/org/traccar/protocol/MegastekProtocol.java +++ b/src/main/java/org/traccar/protocol/MegastekProtocol.java @@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class MegastekProtocol extends BaseProtocol { - public MegastekProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public MegastekProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new MegastekFrameDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/MegastekProtocolDecoder.java b/src/main/java/org/traccar/protocol/MegastekProtocolDecoder.java index 7233280c2..06b6f0e76 100644 --- a/src/main/java/org/traccar/protocol/MegastekProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/MegastekProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/MeiligaoProtocol.java b/src/main/java/org/traccar/protocol/MeiligaoProtocol.java index e8a66e49f..d86a00fb3 100644 --- a/src/main/java/org/traccar/protocol/MeiligaoProtocol.java +++ b/src/main/java/org/traccar/protocol/MeiligaoProtocol.java @@ -18,31 +18,36 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class MeiligaoProtocol extends BaseProtocol { - public MeiligaoProtocol() { + @Inject + public MeiligaoProtocol(Config config) { setSupportedDataCommands( Command.TYPE_POSITION_SINGLE, Command.TYPE_POSITION_PERIODIC, + Command.TYPE_OUTPUT_CONTROL, Command.TYPE_ENGINE_STOP, Command.TYPE_ENGINE_RESUME, Command.TYPE_ALARM_GEOFENCE, Command.TYPE_SET_TIMEZONE, Command.TYPE_REQUEST_PHOTO, Command.TYPE_REBOOT_DEVICE); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new MeiligaoFrameDecoder()); pipeline.addLast(new MeiligaoProtocolEncoder(MeiligaoProtocol.this)); pipeline.addLast(new MeiligaoProtocolDecoder(MeiligaoProtocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new MeiligaoProtocolEncoder(MeiligaoProtocol.this)); pipeline.addLast(new MeiligaoProtocolDecoder(MeiligaoProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java index d38e5d1c3..1f8c4d2da 100644 --- a/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java @@ -19,8 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; @@ -40,7 +39,7 @@ import java.util.regex.Pattern; public class MeiligaoProtocolDecoder extends BaseProtocolDecoder { - private Map<Byte, ByteBuf> photos = new HashMap<>(); + private final Map<Byte, ByteBuf> photos = new HashMap<>(); public MeiligaoProtocolDecoder(Protocol protocol) { super(protocol); @@ -283,7 +282,7 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_RSSI, parser.nextHexInt()); position.set(Position.KEY_ODOMETER, parser.nextHexLong()); position.set(Position.KEY_SATELLITES, parser.nextHexInt()); - position.set("driverLicense", parser.next()); + position.set(Position.KEY_CARD, parser.next()); position.set(Position.KEY_ODOMETER, parser.nextLong()); position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next()); @@ -469,10 +468,9 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder { } else if (command == MSG_POSITION_IMAGE) { byte imageIndex = buf.readByte(); buf.readUnsignedByte(); // image upload type - String uniqueId = Context.getIdentityManager().getById(deviceSession.getDeviceId()).getUniqueId(); ByteBuf photo = photos.remove(imageIndex); try { - position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(uniqueId, photo, "jpg")); + position.set(Position.KEY_IMAGE, writeMediaFile(deviceSession.getUniqueId(), photo, "jpg")); } finally { photo.release(); } diff --git a/src/main/java/org/traccar/protocol/MeiligaoProtocolEncoder.java b/src/main/java/org/traccar/protocol/MeiligaoProtocolEncoder.java index e5b2cf4e7..5859d91ce 100644 --- a/src/main/java/org/traccar/protocol/MeiligaoProtocolEncoder.java +++ b/src/main/java/org/traccar/protocol/MeiligaoProtocolEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org) + * 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. @@ -18,13 +18,16 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import org.traccar.BaseProtocolEncoder; -import org.traccar.Context; +import org.traccar.Protocol; +import org.traccar.config.Keys; import org.traccar.helper.Checksum; import org.traccar.helper.DataConverter; +import org.traccar.helper.model.AttributeUtil; import org.traccar.model.Command; -import org.traccar.Protocol; +import org.traccar.model.Device; import java.nio.charset.StandardCharsets; +import java.util.Set; import java.util.TimeZone; public class MeiligaoProtocolEncoder extends BaseProtocolEncoder { @@ -56,15 +59,36 @@ public class MeiligaoProtocolEncoder extends BaseProtocolEncoder { return buf; } - @Override - protected Object encodeCommand(Command command) { + private ByteBuf encodeOutputCommand(long deviceId, int index, int value) { - boolean alternative = Context.getIdentityManager().lookupAttributeBoolean( - command.getDeviceId(), getProtocolName() + ".alternative", false, false, true); + int outputCount; + int outputType; - int outputControlMessageType = alternative - ? MeiligaoProtocolDecoder.MSG_OUTPUT_CONTROL_1 - : MeiligaoProtocolDecoder.MSG_OUTPUT_CONTROL_2; + String model = getCacheManager().getObject(Device.class, deviceId).getModel(); + + if (model != null && Set.of("TK510", "GT08", "TK208", "TK228", "MT05").contains(model)) { + outputCount = 5; + outputType = MeiligaoProtocolDecoder.MSG_OUTPUT_CONTROL_1; + } else { + outputCount = 1; + boolean alternative = AttributeUtil.lookup( + getCacheManager(), Keys.PROTOCOL_ALTERNATIVE.withPrefix(getProtocolName()), deviceId); + outputType = alternative + ? MeiligaoProtocolDecoder.MSG_OUTPUT_CONTROL_1 + : MeiligaoProtocolDecoder.MSG_OUTPUT_CONTROL_2; + } + + ByteBuf content = Unpooled.buffer(); + + for (int i = 1; i <= outputCount; i++) { + content.writeByte(i == index ? value : 2); + } + + return encodeContent(deviceId, outputType, content); + } + + @Override + protected Object encodeCommand(Command command) { ByteBuf content = Unpooled.buffer(); @@ -74,12 +98,14 @@ public class MeiligaoProtocolEncoder extends BaseProtocolEncoder { case Command.TYPE_POSITION_PERIODIC: content.writeShort(command.getInteger(Command.KEY_FREQUENCY) / 10); return encodeContent(command.getDeviceId(), MeiligaoProtocolDecoder.MSG_TRACK_BY_INTERVAL, content); + case Command.TYPE_OUTPUT_CONTROL: + int index = command.getInteger(Command.KEY_INDEX) - 1; + int value = command.getInteger(Command.KEY_DATA); + return encodeOutputCommand(command.getDeviceId(), index, value); case Command.TYPE_ENGINE_STOP: - content.writeByte(0x01); - return encodeContent(command.getDeviceId(), outputControlMessageType, content); + return encodeOutputCommand(command.getDeviceId(), 1, 1); case Command.TYPE_ENGINE_RESUME: - content.writeByte(0x00); - return encodeContent(command.getDeviceId(), outputControlMessageType, content); + return encodeOutputCommand(command.getDeviceId(), 1, 0); case Command.TYPE_ALARM_GEOFENCE: content.writeShort(command.getInteger(Command.KEY_RADIUS)); return encodeContent(command.getDeviceId(), MeiligaoProtocolDecoder.MSG_MOVEMENT_ALARM, content); diff --git a/src/main/java/org/traccar/protocol/MeitrackProtocol.java b/src/main/java/org/traccar/protocol/MeitrackProtocol.java index 7439ea611..4109b22c9 100644 --- a/src/main/java/org/traccar/protocol/MeitrackProtocol.java +++ b/src/main/java/org/traccar/protocol/MeitrackProtocol.java @@ -19,11 +19,15 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class MeitrackProtocol extends BaseProtocol { - public MeitrackProtocol() { + @Inject + public MeitrackProtocol(Config config) { setSupportedDataCommands( Command.TYPE_POSITION_SINGLE, Command.TYPE_ENGINE_STOP, @@ -32,18 +36,18 @@ public class MeitrackProtocol extends BaseProtocol { Command.TYPE_ALARM_DISARM, Command.TYPE_REQUEST_PHOTO, Command.TYPE_SEND_SMS); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new MeitrackFrameDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new MeitrackProtocolEncoder(MeitrackProtocol.this)); pipeline.addLast(new MeitrackProtocolDecoder(MeitrackProtocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringEncoder()); pipeline.addLast(new MeitrackProtocolEncoder(MeitrackProtocol.this)); pipeline.addLast(new MeitrackProtocolDecoder(MeitrackProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java index a9cc1de3a..0f0d22021 100644 --- a/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 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. @@ -19,8 +19,8 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; +import org.traccar.model.Device; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.Checksum; @@ -204,18 +204,18 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { position.set(Position.PREFIX_ADC + i, parser.nextHexInt()); } - String deviceModel = Context.getIdentityManager().getById(deviceSession.getDeviceId()).getModel(); - if (deviceModel == null) { - deviceModel = ""; + String model = getCacheManager().getObject(Device.class, deviceSession.getDeviceId()).getModel(); + if (model == null) { + model = ""; } - switch (deviceModel.toUpperCase()) { + switch (model.toUpperCase()) { case "MVT340": case "MVT380": - position.set(Position.KEY_BATTERY, parser.nextHexInt(0) * 3.0 * 2.0 / 1024.0); + position.set(Position.KEY_BATTERY, parser.nextHexInt() * 3.0 * 2.0 / 1024.0); position.set(Position.KEY_POWER, parser.nextHexInt(0) * 3.0 * 16.0 / 1024.0); break; case "MT90": - position.set(Position.KEY_BATTERY, parser.nextHexInt(0) * 3.3 * 2.0 / 4096.0); + position.set(Position.KEY_BATTERY, parser.nextHexInt() * 3.3 * 2.0 / 4096.0); position.set(Position.KEY_POWER, parser.nextHexInt(0)); break; case "T1": @@ -225,19 +225,18 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { case "MVT800": case "TC68": case "TC68S": - position.set(Position.KEY_BATTERY, parser.nextHexInt(0) * 3.3 * 2.0 / 4096.0); + position.set(Position.KEY_BATTERY, parser.nextHexInt() * 3.3 * 2.0 / 4096.0); position.set(Position.KEY_POWER, parser.nextHexInt(0) * 3.3 * 16.0 / 4096.0); break; case "T311": case "T322X": case "T333": case "T355": - position.set(Position.KEY_BATTERY, parser.nextHexInt(0) / 100.0); - position.set(Position.KEY_POWER, parser.nextHexInt(0) / 100.0); - break; + case "T366": + case "T366G": default: - position.set(Position.KEY_BATTERY, parser.nextHexInt(0)); - position.set(Position.KEY_POWER, parser.nextHexInt(0)); + position.set(Position.KEY_BATTERY, parser.nextHexInt() / 100.0); + position.set(Position.KEY_POWER, parser.nextHexInt(0) / 100.0); break; } @@ -368,13 +367,9 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { StringBuilder command = new StringBuilder("@@"); command.append(flag).append(27 + positions.size() / 10).append(","); command.append(imei).append(",CCC,").append(positions.size()).append("*"); - int checksum = 0; - for (int i = 0; i < command.length(); i += 1) { - checksum += command.charAt(i); - } - command.append(String.format("%02x", checksum & 0xff).toUpperCase()); + command.append(Checksum.sum(command.toString())); command.append("\r\n"); - channel.writeAndFlush(new NetworkMessage(command.toString(), remoteAddress)); // delete processed data + channel.writeAndFlush(new NetworkMessage(command.toString(), remoteAddress)); } return positions; @@ -404,7 +399,8 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { int paramCount = buf.readUnsignedByte(); for (int j = 0; j < paramCount; j++) { - int id = buf.readUnsignedByte(); + boolean extension = buf.getUnsignedByte(buf.readerIndex()) == 0xFE; + int id = extension ? buf.readUnsignedShort() : buf.readUnsignedByte(); switch (id) { case 0x01: position.set(Position.KEY_EVENT, buf.readUnsignedByte()); @@ -424,12 +420,21 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { case 0x15: position.set(Position.KEY_INPUT, buf.readUnsignedByte()); break; + case 0x47: + int lockState = buf.readUnsignedByte(); + if (lockState > 0) { + position.set(Position.KEY_LOCK, lockState == 2); + } + break; case 0x97: position.set(Position.KEY_THROTTLE, buf.readUnsignedByte()); break; case 0x9D: position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedByte()); break; + case 0xFE69: + position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte()); + break; default: buf.readUnsignedByte(); break; @@ -438,7 +443,8 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { paramCount = buf.readUnsignedByte(); for (int j = 0; j < paramCount; j++) { - int id = buf.readUnsignedByte(); + boolean extension = buf.getUnsignedByte(buf.readerIndex()) == 0xFE; + int id = extension ? buf.readUnsignedShort() : buf.readUnsignedByte(); switch (id) { case 0x08: position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShortLE())); @@ -491,7 +497,8 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { paramCount = buf.readUnsignedByte(); for (int j = 0; j < paramCount; j++) { - int id = buf.readUnsignedByte(); + boolean extension = buf.getUnsignedByte(buf.readerIndex()) == 0xFE; + int id = extension ? buf.readUnsignedShort() : buf.readUnsignedByte(); switch (id) { case 0x02: position.setLatitude(buf.readIntLE() * 0.000001); @@ -509,6 +516,9 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { case 0x0D: position.set("runtime", buf.readUnsignedIntLE()); break; + case 0x25: + position.set(Position.KEY_DRIVER_UNIQUE_ID, String.valueOf(buf.readUnsignedIntLE())); + break; case 0xA0: position.set(Position.KEY_FUEL_USED, buf.readUnsignedIntLE() * 0.001); break; @@ -523,8 +533,41 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { paramCount = buf.readUnsignedByte(); for (int j = 0; j < paramCount; j++) { - buf.readUnsignedByte(); // id - buf.skipBytes(buf.readUnsignedByte()); // value + boolean extension = buf.getUnsignedByte(buf.readerIndex()) == 0xFE; + int id = extension ? buf.readUnsignedShort() : buf.readUnsignedByte(); + int length = buf.readUnsignedByte(); + switch (id) { + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + buf.readUnsignedByte(); // label + position.set(Position.PREFIX_TEMP + (id - 0x2A), buf.readShortLE() * 0.01); + break; + case 0xFE31: + buf.readUnsignedByte(); // alarm protocol + buf.readUnsignedByte(); // alarm type + buf.skipBytes(length - 2); + break; + case 0xFEA8: + for (int k = 1; k <= 3; k++) { + if (buf.readUnsignedByte() > 0) { + String key = k == 1 ? Position.KEY_BATTERY_LEVEL : "battery" + k + "Level"; + position.set(key, buf.readUnsignedByte()); + } else { + buf.readUnsignedByte(); + } + } + buf.readUnsignedByte(); // battery alert + break; + default: + buf.skipBytes(length); + break; + } } positions.add(position); @@ -533,13 +576,13 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { return positions; } - private void requestPhotoPacket(Channel channel, SocketAddress socketAddress, String imei, String file, int index) { + private void requestPhotoPacket(Channel channel, SocketAddress remoteAddress, String imei, String file, int index) { if (channel != null) { String content = "D00," + file + "," + index; int length = 1 + imei.length() + 1 + content.length() + 5; String response = String.format("@@O%02d,%s,%s*", length, imei, content); response += Checksum.sum(response) + "\r\n"; - channel.writeAndFlush(new NetworkMessage(response, socketAddress)); + channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); } } @@ -555,6 +598,13 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { String type = buf.toString(index + 1, 3, StandardCharsets.US_ASCII); switch (type) { + case "AAC": + if (channel != null) { + String response = String.format("@@z27,%s,AAC,1*", imei); + response += Checksum.sum(response) + "\r\n"; + channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); + } + return null; case "D00": if (photo == null) { photo = Unpooled.buffer(); @@ -579,7 +629,7 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { getLastLocation(position, null); - position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(imei, photo, "jpg")); + position.set(Position.KEY_IMAGE, writeMediaFile(imei, photo, "jpg")); photo.release(); photo = null; @@ -594,6 +644,13 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { photo = Unpooled.buffer(); requestPhotoPacket(channel, remoteAddress, imei, "camera_picture.jpg", 0); return null; + case "D82": + Position position = new Position(getProtocolName()); + position.setDeviceId(getDeviceSession(channel, remoteAddress, imei).getDeviceId()); + getLastLocation(position, null); + String result = buf.toString(index + 1, buf.writerIndex() - index - 4, StandardCharsets.US_ASCII); + position.set(Position.KEY_RESULT, result); + return position; case "CCC": return decodeBinaryC(channel, remoteAddress, buf); case "CCE": diff --git a/src/main/java/org/traccar/protocol/MeitrackProtocolEncoder.java b/src/main/java/org/traccar/protocol/MeitrackProtocolEncoder.java index 354e81434..365dbb35a 100644 --- a/src/main/java/org/traccar/protocol/MeitrackProtocolEncoder.java +++ b/src/main/java/org/traccar/protocol/MeitrackProtocolEncoder.java @@ -15,11 +15,12 @@ */ package org.traccar.protocol; -import org.traccar.Context; +import org.traccar.Protocol; import org.traccar.StringProtocolEncoder; +import org.traccar.config.Keys; import org.traccar.helper.Checksum; +import org.traccar.helper.model.AttributeUtil; import org.traccar.model.Command; -import org.traccar.Protocol; import java.util.Map; @@ -42,8 +43,8 @@ public class MeitrackProtocolEncoder extends StringProtocolEncoder { Map<String, Object> attributes = command.getAttributes(); - boolean alternative = Context.getIdentityManager().lookupAttributeBoolean( - command.getDeviceId(), getProtocolName() + ".alternative", false, false, true); + boolean alternative = AttributeUtil.lookup( + getCacheManager(), Keys.PROTOCOL_ALTERNATIVE.withPrefix(getProtocolName()), command.getDeviceId()); switch (command.getType()) { case Command.TYPE_POSITION_SINGLE: diff --git a/src/main/java/org/traccar/protocol/MictrackProtocol.java b/src/main/java/org/traccar/protocol/MictrackProtocol.java index 9fd9666e4..08bbe0c82 100644 --- a/src/main/java/org/traccar/protocol/MictrackProtocol.java +++ b/src/main/java/org/traccar/protocol/MictrackProtocol.java @@ -20,21 +20,25 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class MictrackProtocol extends BaseProtocol { - public MictrackProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public MictrackProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new MictrackProtocolDecoder(MictrackProtocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new MictrackProtocolDecoder(MictrackProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java index c72a742b9..84ba75e7c 100644 --- a/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java @@ -18,7 +18,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/MilesmateProtocol.java b/src/main/java/org/traccar/protocol/MilesmateProtocol.java index 822711603..607dfc5bf 100644 --- a/src/main/java/org/traccar/protocol/MilesmateProtocol.java +++ b/src/main/java/org/traccar/protocol/MilesmateProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class MilesmateProtocol extends BaseProtocol { - public MilesmateProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public MilesmateProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/MilesmateProtocolDecoder.java b/src/main/java/org/traccar/protocol/MilesmateProtocolDecoder.java index 901ceb8f7..21c629411 100644 --- a/src/main/java/org/traccar/protocol/MilesmateProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/MilesmateProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; diff --git a/src/main/java/org/traccar/protocol/MiniFinderProtocol.java b/src/main/java/org/traccar/protocol/MiniFinderProtocol.java index 0cc9598ed..1cb2a0007 100644 --- a/src/main/java/org/traccar/protocol/MiniFinderProtocol.java +++ b/src/main/java/org/traccar/protocol/MiniFinderProtocol.java @@ -21,11 +21,15 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class MiniFinderProtocol extends BaseProtocol { - public MiniFinderProtocol() { + @Inject + public MiniFinderProtocol(Config config) { setSupportedDataCommands( Command.TYPE_SET_TIMEZONE, Command.TYPE_VOICE_MONITORING, @@ -38,9 +42,9 @@ public class MiniFinderProtocol extends BaseProtocol { Command.TYPE_MODE_DEEP_SLEEP, Command.TYPE_SOS_NUMBER, Command.TYPE_SET_INDICATOR); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ";\0", ";")); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/MiniFinderProtocolDecoder.java b/src/main/java/org/traccar/protocol/MiniFinderProtocolDecoder.java index d5be31cec..1fdb1ece0 100644 --- a/src/main/java/org/traccar/protocol/MiniFinderProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/MiniFinderProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.BitUtil; import org.traccar.helper.Parser; @@ -143,7 +143,7 @@ public class MiniFinderProtocolDecoder extends BaseProtocolDecoder { } DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); - if (deviceSession == null || !sentence.matches("![35A-D],.*")) { + if (deviceSession == null || !sentence.matches("![345A-D],.*")) { return null; } @@ -161,6 +161,20 @@ public class MiniFinderProtocolDecoder extends BaseProtocolDecoder { return position; + } else if (type.equals("4")) { + + String[] values = sentence.split(","); + + getLastLocation(position, null); + + for (int i = 1; i <= 3; i++) { + if (!values[i + 1].isEmpty()) { + position.set("phone" + i, values[i + 1]); + } + } + + return position; + } else if (type.equals("5")) { String[] values = sentence.split(","); diff --git a/src/main/java/org/traccar/protocol/Minifinder2Protocol.java b/src/main/java/org/traccar/protocol/Minifinder2Protocol.java index f8801db74..c12933b81 100644 --- a/src/main/java/org/traccar/protocol/Minifinder2Protocol.java +++ b/src/main/java/org/traccar/protocol/Minifinder2Protocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2019 - 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. @@ -19,16 +19,24 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; +import org.traccar.model.Command; import java.nio.ByteOrder; +import jakarta.inject.Inject; + public class Minifinder2Protocol extends BaseProtocol { - public Minifinder2Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public Minifinder2Protocol(Config config) { + setSupportedDataCommands( + Command.TYPE_FIRMWARE_UPDATE); + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { - pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1200, 2, 2, 4, 0, true)); + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 2048, 2, 2, 4, 0, true)); + pipeline.addLast(new Minifinder2ProtocolEncoder(Minifinder2Protocol.this)); pipeline.addLast(new Minifinder2ProtocolDecoder(Minifinder2Protocol.this)); } }); diff --git a/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java index 641a45864..6289bd2eb 100644 --- a/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2019 - 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. @@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; @@ -48,9 +48,11 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_DATA = 0x01; public static final int MSG_CONFIGURATION = 0x02; public static final int MSG_SERVICES = 0x03; + public static final int MSG_SYSTEM_CONTROL = 0x04; + public static final int MSG_FIRMWARE = 0x7E; public static final int MSG_RESPONSE = 0x7F; - private String decodeAlarm(int code) { + private String decodeAlarm(long code) { if (BitUtil.check(code, 0)) { return Position.ALARM_LOW_BATTERY; } @@ -146,10 +148,10 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { int type = buf.readUnsignedByte(); if (BitUtil.check(flags, 4)) { - sendResponse(channel, remoteAddress, index, type, buf); + sendResponse(channel, remoteAddress, index, type, buf.slice()); } - if (type == MSG_DATA) { + if (type == MSG_DATA || type == MSG_SERVICES) { List<Position> positions = new LinkedList<>(); Set<Integer> keys = new HashSet<>(); @@ -177,11 +179,16 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { case 0x01: deviceSession = getDeviceSession( channel, remoteAddress, buf.readCharSequence(15, StandardCharsets.US_ASCII).toString()); - - position.setDeviceId(deviceSession.getDeviceId()); + if (deviceSession == null) { + return null; + } break; case 0x02: - position.set(Position.KEY_ALARM, decodeAlarm(buf.readIntLE())); + long alarm = buf.readUnsignedIntLE(); + position.set(Position.KEY_ALARM, decodeAlarm(alarm)); + if (BitUtil.check(alarm, 31)) { + position.set("bark", true); + } break; case 0x14: position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte()); @@ -194,7 +201,9 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShortLE())); position.setCourse(buf.readUnsignedShortLE()); position.setAltitude(buf.readShortLE()); - position.setValid(buf.readUnsignedShortLE() > 0); + int hdop = buf.readUnsignedShortLE(); + position.setValid(hdop > 0); + position.set(Position.KEY_HDOP, hdop * 0.1); position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE()); position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); break; @@ -231,6 +240,14 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { case 0x24: position.setTime(new Date(buf.readUnsignedIntLE() * 1000)); long status = buf.readUnsignedIntLE(); + if (BitUtil.check(status, 4)) { + position.set(Position.KEY_CHARGE, true); + } + if (BitUtil.check(status, 7)) { + position.set(Position.KEY_ARCHIVE, true); + } + position.set(Position.KEY_MOTION, BitUtil.check(status, 9)); + position.set(Position.KEY_RSSI, BitUtil.between(status, 19, 24)); position.set(Position.KEY_BATTERY_LEVEL, BitUtil.from(status, 24)); position.set(Position.KEY_STATUS, status); break; @@ -260,8 +277,24 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { hasLocation = true; break; case 0x30: - buf.readUnsignedInt(); // timestamp - position.set(Position.KEY_STEPS, buf.readUnsignedInt()); + buf.readUnsignedIntLE(); // timestamp + position.set(Position.KEY_STEPS, buf.readUnsignedIntLE()); + break; + case 0x31: + int i = 1; + while (buf.readerIndex() < endIndex) { + position.set("activity" + i + "Time", buf.readUnsignedIntLE()); + position.set("activity" + i, buf.readUnsignedIntLE()); + i += 1; + } + break; + case 0x37: + buf.readUnsignedIntLE(); // timestamp + long barking = buf.readUnsignedIntLE(); + if (BitUtil.check(barking, 31)) { + position.set("barkStop", true); + } + position.set("barkCount", BitUtil.to(barking, 31)); break; case 0x40: buf.readUnsignedIntLE(); // timestamp @@ -291,6 +324,23 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { return positions; + } else if (type == MSG_RESPONSE) { + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + getLastLocation(position, null); + + buf.readUnsignedByte(); // length + position.set(Position.KEY_RESULT, String.valueOf(buf.readUnsignedByte())); + + return position; + } return null; diff --git a/src/main/java/org/traccar/protocol/Minifinder2ProtocolEncoder.java b/src/main/java/org/traccar/protocol/Minifinder2ProtocolEncoder.java new file mode 100644 index 000000000..fab3c3a6d --- /dev/null +++ b/src/main/java/org/traccar/protocol/Minifinder2ProtocolEncoder.java @@ -0,0 +1,67 @@ +/* + * 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.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import org.traccar.BaseProtocolEncoder; +import org.traccar.Protocol; +import org.traccar.helper.Checksum; +import org.traccar.model.Command; +import org.traccar.model.Device; + +import java.nio.charset.StandardCharsets; + +public class Minifinder2ProtocolEncoder extends BaseProtocolEncoder { + + public Minifinder2ProtocolEncoder(Protocol protocol) { + super(protocol); + } + + private ByteBuf encodeContent(ByteBuf content) { + + ByteBuf buf = Unpooled.buffer(); + + buf.writeByte(0xAB); // header + buf.writeByte(0x00); // properties + buf.writeShortLE(content.readableBytes()); + buf.writeShortLE(Checksum.crc16(Checksum.CRC16_XMODEM, content.nioBuffer())); + buf.writeShortLE(1); // index + buf.writeBytes(content); + + return buf; + } + + @Override + protected Object encodeCommand(Command command) { + + Device device = getCacheManager().getObject(Device.class, command.getDeviceId()); + if ("Nano".equalsIgnoreCase(device.getModel())) { + ByteBuf content = Unpooled.buffer(); + if (command.getType().equals(Command.TYPE_FIRMWARE_UPDATE)) { + String url = command.getString(Command.KEY_DATA); + content.writeByte(Minifinder2ProtocolDecoder.MSG_SYSTEM_CONTROL); + content.writeByte(1 + url.length()); + content.writeByte(0x30); // type + content.writeCharSequence(url, StandardCharsets.US_ASCII); + return encodeContent(content); + } + } + + return null; + } + +} diff --git a/src/main/java/org/traccar/protocol/MobilogixProtocol.java b/src/main/java/org/traccar/protocol/MobilogixProtocol.java index 28380a2af..36d6b5ed2 100644 --- a/src/main/java/org/traccar/protocol/MobilogixProtocol.java +++ b/src/main/java/org/traccar/protocol/MobilogixProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class MobilogixProtocol extends BaseProtocol { - public MobilogixProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public MobilogixProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ']')); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/MobilogixProtocolDecoder.java b/src/main/java/org/traccar/protocol/MobilogixProtocolDecoder.java index 86c89e336..d7600ecbb 100644 --- a/src/main/java/org/traccar/protocol/MobilogixProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/MobilogixProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/MoovboxProtocol.java b/src/main/java/org/traccar/protocol/MoovboxProtocol.java index 7b554266f..af853fe67 100644 --- a/src/main/java/org/traccar/protocol/MoovboxProtocol.java +++ b/src/main/java/org/traccar/protocol/MoovboxProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class MoovboxProtocol extends BaseProtocol { - public MoovboxProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public MoovboxProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new HttpResponseEncoder()); pipeline.addLast(new HttpRequestDecoder()); pipeline.addLast(new HttpObjectAggregator(65535)); diff --git a/src/main/java/org/traccar/protocol/MoovboxProtocolDecoder.java b/src/main/java/org/traccar/protocol/MoovboxProtocolDecoder.java index 3116d073c..8e6679b05 100644 --- a/src/main/java/org/traccar/protocol/MoovboxProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/MoovboxProtocolDecoder.java @@ -20,7 +20,7 @@ import io.netty.channel.Channel; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpResponseStatus; import org.traccar.BaseHttpProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.model.Position; import org.w3c.dom.Document; diff --git a/src/main/java/org/traccar/protocol/MotorProtocol.java b/src/main/java/org/traccar/protocol/MotorProtocol.java index 680687e15..f17886577 100644 --- a/src/main/java/org/traccar/protocol/MotorProtocol.java +++ b/src/main/java/org/traccar/protocol/MotorProtocol.java @@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class MotorProtocol extends BaseProtocol { - public MotorProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public MotorProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringDecoder()); pipeline.addLast(new MotorProtocolDecoder(MotorProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/MotorProtocolDecoder.java b/src/main/java/org/traccar/protocol/MotorProtocolDecoder.java index 8ce4fe8b1..9bca4d9bc 100644 --- a/src/main/java/org/traccar/protocol/MotorProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/MotorProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.BcdUtil; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/Mta6Protocol.java b/src/main/java/org/traccar/protocol/Mta6Protocol.java index 14a66ce5c..c1c6eb829 100644 --- a/src/main/java/org/traccar/protocol/Mta6Protocol.java +++ b/src/main/java/org/traccar/protocol/Mta6Protocol.java @@ -19,22 +19,25 @@ import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpRequestDecoder; import io.netty.handler.codec.http.HttpResponseEncoder; import org.traccar.BaseProtocol; -import org.traccar.Context; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.config.Keys; +import jakarta.inject.Inject; + public class Mta6Protocol extends BaseProtocol { - public Mta6Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public Mta6Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new HttpResponseEncoder()); pipeline.addLast(new HttpRequestDecoder()); pipeline.addLast(new HttpObjectAggregator(65535)); pipeline.addLast(new Mta6ProtocolDecoder( - Mta6Protocol.this, !Context.getConfig().getBoolean(Keys.PROTOCOL_CAN.withPrefix(getName())))); + Mta6Protocol.this, !config.getBoolean(Keys.PROTOCOL_CAN.withPrefix(getName())))); } }); } diff --git a/src/main/java/org/traccar/protocol/Mta6ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Mta6ProtocolDecoder.java index 88419b871..896c7a2d2 100644 --- a/src/main/java/org/traccar/protocol/Mta6ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Mta6ProtocolDecoder.java @@ -26,7 +26,7 @@ import io.netty.handler.codec.http.HttpVersion; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/MtxProtocol.java b/src/main/java/org/traccar/protocol/MtxProtocol.java index 44372ce83..12d324019 100644 --- a/src/main/java/org/traccar/protocol/MtxProtocol.java +++ b/src/main/java/org/traccar/protocol/MtxProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class MtxProtocol extends BaseProtocol { - public MtxProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public MtxProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/MtxProtocolDecoder.java b/src/main/java/org/traccar/protocol/MtxProtocolDecoder.java index d1207bedf..e94d12b36 100644 --- a/src/main/java/org/traccar/protocol/MtxProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/MtxProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/MxtProtocol.java b/src/main/java/org/traccar/protocol/MxtProtocol.java index dbe43fe45..2f0cc1658 100644 --- a/src/main/java/org/traccar/protocol/MxtProtocol.java +++ b/src/main/java/org/traccar/protocol/MxtProtocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class MxtProtocol extends BaseProtocol { - public MxtProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public MxtProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new MxtFrameDecoder()); pipeline.addLast(new MxtProtocolDecoder(MxtProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/MxtProtocolDecoder.java b/src/main/java/org/traccar/protocol/MxtProtocolDecoder.java index 379b610e1..b3e2295e8 100644 --- a/src/main/java/org/traccar/protocol/MxtProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/MxtProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/NavigilProtocol.java b/src/main/java/org/traccar/protocol/NavigilProtocol.java index 2c946c39f..a309235c5 100644 --- a/src/main/java/org/traccar/protocol/NavigilProtocol.java +++ b/src/main/java/org/traccar/protocol/NavigilProtocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class NavigilProtocol extends BaseProtocol { - public NavigilProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public NavigilProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new NavigilFrameDecoder()); pipeline.addLast(new NavigilProtocolDecoder(NavigilProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/NavigilProtocolDecoder.java b/src/main/java/org/traccar/protocol/NavigilProtocolDecoder.java index db5521201..6dadbc559 100644 --- a/src/main/java/org/traccar/protocol/NavigilProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/NavigilProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.Checksum; diff --git a/src/main/java/org/traccar/protocol/NavisProtocol.java b/src/main/java/org/traccar/protocol/NavisProtocol.java index d5af6838d..96b5b0de0 100644 --- a/src/main/java/org/traccar/protocol/NavisProtocol.java +++ b/src/main/java/org/traccar/protocol/NavisProtocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class NavisProtocol extends BaseProtocol { - public NavisProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public NavisProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new NavisFrameDecoder()); pipeline.addLast(new NavisProtocolDecoder(NavisProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/NavisProtocolDecoder.java b/src/main/java/org/traccar/protocol/NavisProtocolDecoder.java index 7ba474ae0..77158b315 100644 --- a/src/main/java/org/traccar/protocol/NavisProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/NavisProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,12 +19,11 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; import org.traccar.helper.Checksum; -import org.traccar.helper.Checksum.Algorithm; import org.traccar.helper.DateBuilder; import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; @@ -591,10 +590,8 @@ public class NavisProtocolDecoder extends BaseProtocolDecoder { private void sendFlexReply(Channel channel, ByteBuf data) { if (channel != null) { - ByteBuf cs = Unpooled.buffer(1); - cs.writeByte(Checksum.crc8(new Algorithm(8, 0x31, 0xFF, false, false, 0x00), data.nioBuffer())); - - channel.writeAndFlush(new NetworkMessage(Unpooled.wrappedBuffer(data, cs), channel.remoteAddress())); + data.writeByte(Checksum.crc8(Checksum.CRC8_EGTS, data.nioBuffer())); + channel.writeAndFlush(new NetworkMessage(data, channel.remoteAddress())); } } diff --git a/src/main/java/org/traccar/protocol/NavisetProtocol.java b/src/main/java/org/traccar/protocol/NavisetProtocol.java index 78755ea4d..6df0b0436 100644 --- a/src/main/java/org/traccar/protocol/NavisetProtocol.java +++ b/src/main/java/org/traccar/protocol/NavisetProtocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class NavisetProtocol extends BaseProtocol { - public NavisetProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public NavisetProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new NavisetFrameDecoder()); pipeline.addLast(new NavisetProtocolDecoder(NavisetProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/NavisetProtocolDecoder.java b/src/main/java/org/traccar/protocol/NavisetProtocolDecoder.java index 10d71d76c..47d10b310 100644 --- a/src/main/java/org/traccar/protocol/NavisetProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/NavisetProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/NavtelecomProtocol.java b/src/main/java/org/traccar/protocol/NavtelecomProtocol.java index 29ce8c41e..de5f93df1 100644 --- a/src/main/java/org/traccar/protocol/NavtelecomProtocol.java +++ b/src/main/java/org/traccar/protocol/NavtelecomProtocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class NavtelecomProtocol extends BaseProtocol { - public NavtelecomProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public NavtelecomProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new NavtelecomFrameDecoder()); pipeline.addLast(new NavtelecomProtocolDecoder(NavtelecomProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java b/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java index ecaa6cbf4..87cccbeaa 100644 --- a/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; @@ -196,7 +196,7 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder { for (int j = 0; j < bits.length(); j++) { if (bits.get(j)) { - int value = 0; + int value; switch (j + 1) { case 1: @@ -278,11 +278,11 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder { case 42: case 43: value = buf.readUnsignedShortLE(); - position.set("rs485Fuel" + (j + 2 - 38), (value < 65500) ? value : null); + position.set("fuel" + (j + 2 - 38), (value < 65500) ? value : null); break; case 44: value = buf.readUnsignedShortLE(); - position.set("rs232Fuel", (value < 65500) ? value : null); + position.set(Position.KEY_FUEL_LEVEL, (value < 65500) ? value : null); break; case 45: case 46: @@ -293,7 +293,17 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder { case 51: case 52: value = buf.readByte(); - position.set(Position.PREFIX_TEMP + (j + 2 - 45), (value != 0x80) ? value : null); + position.set( + Position.PREFIX_TEMP + (j + 2 - 45), + (value != (byte) 0x80) ? value : null); + break; + case 78: + case 79: + case 80: + case 81: + case 82: + case 83: + position.set("fuelTemp" + (j + 2 - 78), (int) buf.readByte()); break; case 53: value = buf.readUnsignedShortLE(); diff --git a/src/main/java/org/traccar/protocol/NdtpV6Protocol.java b/src/main/java/org/traccar/protocol/NdtpV6Protocol.java new file mode 100644 index 000000000..9493132f5 --- /dev/null +++ b/src/main/java/org/traccar/protocol/NdtpV6Protocol.java @@ -0,0 +1,39 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.handler.codec.string.StringEncoder; +import org.traccar.BaseProtocol; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; + +public class NdtpV6Protocol extends BaseProtocol { + + @Inject + public NdtpV6Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(new StringEncoder()); + pipeline.addLast(new NdtpV6ProtocolDecoder(NdtpV6Protocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/NdtpV6ProtocolDecoder.java b/src/main/java/org/traccar/protocol/NdtpV6ProtocolDecoder.java new file mode 100644 index 000000000..35cb0bae8 --- /dev/null +++ b/src/main/java/org/traccar/protocol/NdtpV6ProtocolDecoder.java @@ -0,0 +1,203 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.NetworkMessage; +import org.traccar.Protocol; +import org.traccar.helper.BitUtil; +import org.traccar.helper.Checksum; +import org.traccar.helper.UnitsConverter; +import org.traccar.model.Position; +import org.traccar.session.DeviceSession; + +import java.net.SocketAddress; +import java.util.Date; + +public class NdtpV6ProtocolDecoder extends BaseProtocolDecoder { + + public NdtpV6ProtocolDecoder(Protocol protocol) { + super(protocol); + } + + private static final byte[] SIGNATURE = {0x7E, 0x7E}; + + private static final int NPL_FLAG_CRC = 2; + private static final int NPH_RESULT_OK = 0x00000000; + private static final int NPL_TYPE_NPH = 2; + private static final int NPL_ADDRESS_SERVER = 0; + + private static final int NPH_RESULT = 0; + + private static final int NPH_SRV_GENERIC_CONTROLS = 0; + private static final int NPH_SRV_NAVDATA = 1; + + private static final int NPH_SGC_RESULT = NPH_RESULT; + private static final int NPH_SGC_CONN_REQUEST = 100; + + private static final int NPH_SND_RESULT = NPH_RESULT; + + private void sendResponse( + Channel channel, int serviceId, long requestId) { + + ByteBuf content = Unpooled.buffer(); + content.writeShortLE(serviceId); + content.writeIntLE(NPH_SND_RESULT); + content.writeIntLE((int) requestId); + content.writeIntLE(NPH_RESULT_OK); + + ByteBuf response = Unpooled.buffer(); + response.writeBytes(SIGNATURE); + response.writeShortLE(content.readableBytes()); + response.writeShortLE(NPL_FLAG_CRC); // flags + response.writeShort(Checksum.crc16(Checksum.CRC16_MODBUS, content.nioBuffer())); + response.writeByte(NPL_TYPE_NPH); // type + response.writeIntLE(NPL_ADDRESS_SERVER); // peer address + response.writeShortLE(0); // request id + response.writeBytes(content); + content.release(); + + channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress())); + } + + private static final short MAIN_NAV_DATA = 0; + private static final short ADDITIONAL_NAV_DATA = 2; + + private void decodeData(ByteBuf buf, Position position) { + + short itemType; + short itemIndex; + + itemType = buf.readUnsignedByte(); + itemIndex = buf.readUnsignedByte(); + if (itemType == MAIN_NAV_DATA && (itemIndex == 0 || itemIndex == 1)) { + + position.setTime(new Date(buf.readUnsignedIntLE() * 1000)); + position.setLongitude(buf.readIntLE() / 10000000.0); + position.setLatitude(buf.readIntLE() / 10000000.0); + + short flags = buf.readUnsignedByte(); + position.setValid(BitUtil.check(flags, 7)); + if (BitUtil.check(flags, 1)) { + position.set(Position.KEY_ALARM, Position.ALARM_GENERAL); + } + + position.set(Position.KEY_BATTERY, buf.readUnsignedByte() * 20); + position.set(Position.KEY_OBD_SPEED, buf.readUnsignedShortLE()); + position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShortLE())); + position.setCourse(buf.readUnsignedShortLE()); + + position.set(Position.KEY_ODOMETER, buf.readUnsignedShortLE()); + position.setAltitude(buf.readShortLE()); + position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); + position.set(Position.KEY_PDOP, buf.readUnsignedByte()); + } + + itemType = buf.readUnsignedByte(); + itemIndex = buf.readUnsignedByte(); + if (itemType == ADDITIONAL_NAV_DATA && itemIndex == 0) { + + position.set(Position.KEY_BATTERY_LEVEL, Math.max((buf.readUnsignedShortLE() - 3600) / 6, 100)); + position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShortLE()); + position.set(Position.PREFIX_ADC + 3, buf.readUnsignedShortLE()); + position.set(Position.PREFIX_ADC + 4, buf.readUnsignedShortLE()); + + buf.readUnsignedByte(); // inputs + buf.readUnsignedByte(); // outputs + buf.readUnsignedShortLE(); // in1 count + buf.readUnsignedShortLE(); // in2 count + buf.readUnsignedShortLE(); // in3 count + buf.readUnsignedShortLE(); // in4 count + buf.readUnsignedIntLE(); // track length + + position.set(Position.KEY_ANTENNA, buf.readUnsignedByte()); + position.set(Position.KEY_GPS, buf.readUnsignedByte()); + position.set(Position.KEY_ACCELERATION, buf.readUnsignedByte()); + position.set(Position.KEY_POWER, buf.readUnsignedByte() * 200); + } + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + ByteBuf buf = (ByteBuf) msg; + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); + + buf.skipBytes(2); // signature + buf.readUnsignedShortLE(); // length + buf.readUnsignedShortLE(); // connection flags + buf.readUnsignedShortLE(); // checksum + buf.readUnsignedByte(); // type + buf.readUnsignedIntLE(); // address + buf.readUnsignedShortLE(); // identification + + int serviceId = buf.readUnsignedShortLE(); + int serviceType = buf.readUnsignedShortLE(); + buf.readUnsignedShortLE(); // request flags + long requestId = buf.readUnsignedIntLE(); + + if (deviceSession == null && serviceId == NPH_SRV_GENERIC_CONTROLS && serviceType == NPH_SGC_CONN_REQUEST) { + + buf.readUnsignedShortLE(); // version major + buf.readUnsignedShortLE(); // version minor + buf.readUnsignedShortLE(); // connection flags + + int deviceId = buf.readUnsignedShortLE(); + Position position = new Position(getProtocolName()); + deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(deviceId)); + position.setDeviceId(deviceSession.getDeviceId()); + + if (channel != null) { + sendResponse(channel, serviceId, requestId); + } + + position.set(Position.KEY_RESULT, String.valueOf(NPH_SGC_RESULT)); + position.setTime(new Date()); + getLastLocation(position, new Date()); + position.setValid(false); + + return position; + + } + + if (serviceId == NPH_SRV_NAVDATA) { + + deviceSession = getDeviceSession(channel, remoteAddress); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + if (channel != null) { + sendResponse(channel, serviceId, requestId); + } + + decodeData(buf, position); + + return position; + } + + return null; + } + +} diff --git a/src/main/java/org/traccar/protocol/NdtpV6ProtocolEncoder.java b/src/main/java/org/traccar/protocol/NdtpV6ProtocolEncoder.java new file mode 100644 index 000000000..7aac8658a --- /dev/null +++ b/src/main/java/org/traccar/protocol/NdtpV6ProtocolEncoder.java @@ -0,0 +1,42 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import org.traccar.BaseProtocolEncoder; +import org.traccar.Protocol; +import org.traccar.model.Command; + +public class NdtpV6ProtocolEncoder extends BaseProtocolEncoder { + + public NdtpV6ProtocolEncoder(Protocol protocol) { + super(protocol); + } + + @Override + protected Object encodeCommand(Command command) { + switch (command.getType()) { + case Command.TYPE_IDENTIFICATION: + return "BB+IDNT"; + case Command.TYPE_REBOOT_DEVICE: + return "BB+RESET"; + case Command.TYPE_POSITION_SINGLE: + return "BB+RRCD"; + default: + return null; + } + } + +} diff --git a/src/main/java/org/traccar/protocol/NeosProtocol.java b/src/main/java/org/traccar/protocol/NeosProtocol.java index e545a9969..16a6ba5a0 100644 --- a/src/main/java/org/traccar/protocol/NeosProtocol.java +++ b/src/main/java/org/traccar/protocol/NeosProtocol.java @@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class NeosProtocol extends BaseProtocol { - public NeosProtocol() { - addServer(new TrackerServer(true, getName()) { + @Inject + public NeosProtocol(Config config) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new NeosProtocolDecoder(NeosProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/NeosProtocolDecoder.java b/src/main/java/org/traccar/protocol/NeosProtocolDecoder.java index 6b5596dba..18ebc49da 100644 --- a/src/main/java/org/traccar/protocol/NeosProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/NeosProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/NetProtocol.java b/src/main/java/org/traccar/protocol/NetProtocol.java index c114d19fc..e011660da 100644 --- a/src/main/java/org/traccar/protocol/NetProtocol.java +++ b/src/main/java/org/traccar/protocol/NetProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class NetProtocol extends BaseProtocol { - public NetProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public NetProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '!')); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/NetProtocolDecoder.java b/src/main/java/org/traccar/protocol/NetProtocolDecoder.java index c71a792a2..ebffb06f1 100644 --- a/src/main/java/org/traccar/protocol/NetProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/NetProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.BitUtil; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/NiotProtocol.java b/src/main/java/org/traccar/protocol/NiotProtocol.java index b57b18a3a..7eacd5ff3 100644 --- a/src/main/java/org/traccar/protocol/NiotProtocol.java +++ b/src/main/java/org/traccar/protocol/NiotProtocol.java @@ -19,13 +19,17 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class NiotProtocol extends BaseProtocol { - public NiotProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public NiotProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 3, 2)); pipeline.addLast(new NiotProtocolDecoder(NiotProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/NiotProtocolDecoder.java b/src/main/java/org/traccar/protocol/NiotProtocolDecoder.java index 47c6e2ffd..35614ccca 100644 --- a/src/main/java/org/traccar/protocol/NiotProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/NiotProtocolDecoder.java @@ -20,7 +20,8 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.helper.BufferUtil; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BcdUtil; @@ -57,12 +58,6 @@ public class NiotProtocolDecoder extends BaseProtocolDecoder { } } - private double readCoordinate(ByteBuf buf) { - long value = buf.readUnsignedInt(); - double result = BitUtil.to(value, 31) / 1800000.0; - return BitUtil.check(value, 31) ? -result : result; - } - @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { @@ -96,8 +91,8 @@ public class NiotProtocolDecoder extends BaseProtocolDecoder { .setSecond(BcdUtil.readInteger(buf, 2)); position.setTime(dateBuilder.getDate()); - position.setLatitude(readCoordinate(buf)); - position.setLongitude(readCoordinate(buf)); + position.setLatitude(BufferUtil.readSignedMagnitudeInt(buf) / 1800000.0); + position.setLongitude(BufferUtil.readSignedMagnitudeInt(buf) / 1800000.0); BcdUtil.readInteger(buf, 4); // reserved position.setCourse(BcdUtil.readInteger(buf, 4)); diff --git a/src/main/java/org/traccar/protocol/NoranProtocol.java b/src/main/java/org/traccar/protocol/NoranProtocol.java index 3df364c30..d03e52be5 100644 --- a/src/main/java/org/traccar/protocol/NoranProtocol.java +++ b/src/main/java/org/traccar/protocol/NoranProtocol.java @@ -18,20 +18,24 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class NoranProtocol extends BaseProtocol { - public NoranProtocol() { + @Inject + public NoranProtocol(Config config) { setSupportedDataCommands( Command.TYPE_POSITION_SINGLE, Command.TYPE_POSITION_PERIODIC, Command.TYPE_POSITION_STOP, Command.TYPE_ENGINE_STOP, Command.TYPE_ENGINE_RESUME); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new NoranProtocolEncoder(NoranProtocol.this)); pipeline.addLast(new NoranProtocolDecoder(NoranProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/NoranProtocolDecoder.java b/src/main/java/org/traccar/protocol/NoranProtocolDecoder.java index 53dae7fd6..53b58f9b6 100644 --- a/src/main/java/org/traccar/protocol/NoranProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/NoranProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/NvsProtocol.java b/src/main/java/org/traccar/protocol/NvsProtocol.java index d319b22f3..8a4ece30d 100644 --- a/src/main/java/org/traccar/protocol/NvsProtocol.java +++ b/src/main/java/org/traccar/protocol/NvsProtocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class NvsProtocol extends BaseProtocol { - public NvsProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public NvsProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new NvsFrameDecoder()); pipeline.addLast(new NvsProtocolDecoder(NvsProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/NvsProtocolDecoder.java b/src/main/java/org/traccar/protocol/NvsProtocolDecoder.java index 5d1159f7d..f826c4121 100644 --- a/src/main/java/org/traccar/protocol/NvsProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/NvsProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.UnitsConverter; diff --git a/src/main/java/org/traccar/protocol/NyitechProtocol.java b/src/main/java/org/traccar/protocol/NyitechProtocol.java index 58974be5c..225b1bd5a 100644 --- a/src/main/java/org/traccar/protocol/NyitechProtocol.java +++ b/src/main/java/org/traccar/protocol/NyitechProtocol.java @@ -19,15 +19,19 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import java.nio.ByteOrder; +import jakarta.inject.Inject; + public class NyitechProtocol extends BaseProtocol { - public NyitechProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public NyitechProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 2, 2, -4, 0, true)); pipeline.addLast(new NyitechProtocolDecoder(NyitechProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/NyitechProtocolDecoder.java b/src/main/java/org/traccar/protocol/NyitechProtocolDecoder.java index 62b41a2ea..49bc5b824 100644 --- a/src/main/java/org/traccar/protocol/NyitechProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/NyitechProtocolDecoder.java @@ -18,7 +18,7 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.BitUtil; import org.traccar.helper.DateBuilder; diff --git a/src/main/java/org/traccar/protocol/ObdDongleProtocol.java b/src/main/java/org/traccar/protocol/ObdDongleProtocol.java index 10a55759b..9fcc35d0d 100644 --- a/src/main/java/org/traccar/protocol/ObdDongleProtocol.java +++ b/src/main/java/org/traccar/protocol/ObdDongleProtocol.java @@ -19,13 +19,17 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class ObdDongleProtocol extends BaseProtocol { - public ObdDongleProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public ObdDongleProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(1099, 20, 2, 3, 0)); pipeline.addLast(new ObdDongleProtocolDecoder(ObdDongleProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/ObdDongleProtocolDecoder.java b/src/main/java/org/traccar/protocol/ObdDongleProtocolDecoder.java index 1c9771ce9..bf0ba6f82 100644 --- a/src/main/java/org/traccar/protocol/ObdDongleProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/ObdDongleProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/OigoProtocol.java b/src/main/java/org/traccar/protocol/OigoProtocol.java index 5056f68aa..3483f8270 100644 --- a/src/main/java/org/traccar/protocol/OigoProtocol.java +++ b/src/main/java/org/traccar/protocol/OigoProtocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class OigoProtocol extends BaseProtocol { - public OigoProtocol() { - addServer(new TrackerServer(true, getName()) { + @Inject + public OigoProtocol(Config config) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new OigoProtocolDecoder(OigoProtocol.this)); } }); diff --git a/src/main/java/org/traccar/protocol/OigoProtocolDecoder.java b/src/main/java/org/traccar/protocol/OigoProtocolDecoder.java index b9cc71e8c..b0c7c3bc6 100644 --- a/src/main/java/org/traccar/protocol/OigoProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/OigoProtocolDecoder.java @@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/OkoProtocol.java b/src/main/java/org/traccar/protocol/OkoProtocol.java index 9571ccc48..6ca6c0e93 100644 --- a/src/main/java/org/traccar/protocol/OkoProtocol.java +++ b/src/main/java/org/traccar/protocol/OkoProtocol.java @@ -20,13 +20,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class OkoProtocol extends BaseProtocol { - public OkoProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public OkoProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '}')); pipeline.addLast(new StringDecoder()); pipeline.addLast(new OkoProtocolDecoder(OkoProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/OkoProtocolDecoder.java b/src/main/java/org/traccar/protocol/OkoProtocolDecoder.java index fa35ab455..3bb62acb9 100644 --- a/src/main/java/org/traccar/protocol/OkoProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/OkoProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/OmnicommProtocol.java b/src/main/java/org/traccar/protocol/OmnicommProtocol.java index 96660cb59..b59b84132 100644 --- a/src/main/java/org/traccar/protocol/OmnicommProtocol.java +++ b/src/main/java/org/traccar/protocol/OmnicommProtocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class OmnicommProtocol extends BaseProtocol { - public OmnicommProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public OmnicommProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new OmnicommFrameDecoder()); pipeline.addLast(new OmnicommProtocolDecoder(OmnicommProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/OmnicommProtocolDecoder.java b/src/main/java/org/traccar/protocol/OmnicommProtocolDecoder.java index f90d1f2b3..9d747032b 100644 --- a/src/main/java/org/traccar/protocol/OmnicommProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/OmnicommProtocolDecoder.java @@ -21,7 +21,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/OpenGtsProtocol.java b/src/main/java/org/traccar/protocol/OpenGtsProtocol.java index 5ef3260c6..24d6de706 100644 --- a/src/main/java/org/traccar/protocol/OpenGtsProtocol.java +++ b/src/main/java/org/traccar/protocol/OpenGtsProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class OpenGtsProtocol extends BaseProtocol { - public OpenGtsProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public OpenGtsProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new HttpResponseEncoder()); pipeline.addLast(new HttpRequestDecoder()); pipeline.addLast(new HttpObjectAggregator(16384)); diff --git a/src/main/java/org/traccar/protocol/OpenGtsProtocolDecoder.java b/src/main/java/org/traccar/protocol/OpenGtsProtocolDecoder.java index b76cbfa85..255a81ae6 100644 --- a/src/main/java/org/traccar/protocol/OpenGtsProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/OpenGtsProtocolDecoder.java @@ -20,7 +20,7 @@ import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.QueryStringDecoder; import org.traccar.BaseHttpProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/OrbcommProtocol.java b/src/main/java/org/traccar/protocol/OrbcommProtocol.java index bdfce3b1e..06b00619c 100644 --- a/src/main/java/org/traccar/protocol/OrbcommProtocol.java +++ b/src/main/java/org/traccar/protocol/OrbcommProtocol.java @@ -21,18 +21,22 @@ import io.netty.handler.codec.http.HttpResponseDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerClient; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class OrbcommProtocol extends BaseProtocol { - public OrbcommProtocol() { - addClient(new TrackerClient(getName()) { + @Inject + public OrbcommProtocol(Config config) { + addClient(new TrackerClient(config, getName()) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new HttpRequestEncoder()); pipeline.addLast(new HttpResponseDecoder()); pipeline.addLast(new HttpObjectAggregator(65535)); pipeline.addLast(new OrbcommProtocolDecoder(OrbcommProtocol.this)); - pipeline.addLast(new OrbcommProtocolPoller(OrbcommProtocol.this)); + pipeline.addLast(new OrbcommProtocolPoller(OrbcommProtocol.this, config)); } }); } diff --git a/src/main/java/org/traccar/protocol/OrbcommProtocolDecoder.java b/src/main/java/org/traccar/protocol/OrbcommProtocolDecoder.java index 7277b1e5f..7ed13d647 100644 --- a/src/main/java/org/traccar/protocol/OrbcommProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/OrbcommProtocolDecoder.java @@ -19,15 +19,15 @@ import io.netty.channel.Channel; import io.netty.handler.codec.http.FullHttpResponse; import org.traccar.BasePipelineFactory; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; -import javax.json.Json; -import javax.json.JsonArray; -import javax.json.JsonObject; -import javax.json.JsonValue; +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.json.JsonValue; import java.io.StringReader; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; @@ -70,8 +70,7 @@ public class OrbcommProtocolDecoder extends BaseProtocolDecoder { JsonArray messages = json.getJsonArray("Messages"); for (int i = 0; i < messages.size(); i++) { JsonObject message = messages.getJsonObject(i); - DeviceSession deviceSession = getDeviceSession( - channel, remoteAddress, true, message.getString("MobileID")); + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, message.getString("MobileID")); if (deviceSession != null) { Position position = new Position(getProtocolName()); diff --git a/src/main/java/org/traccar/protocol/OrbcommProtocolPoller.java b/src/main/java/org/traccar/protocol/OrbcommProtocolPoller.java index 6a2d7a92d..0f57bfb49 100644 --- a/src/main/java/org/traccar/protocol/OrbcommProtocolPoller.java +++ b/src/main/java/org/traccar/protocol/OrbcommProtocolPoller.java @@ -24,8 +24,8 @@ import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.QueryStringEncoder; import org.traccar.BaseProtocolPoller; -import org.traccar.Context; import org.traccar.Protocol; +import org.traccar.config.Config; import org.traccar.config.Keys; import java.net.SocketAddress; @@ -46,11 +46,11 @@ public class OrbcommProtocolPoller extends BaseProtocolPoller { this.startTime = startTime; } - public OrbcommProtocolPoller(Protocol protocol) { - super(Context.getConfig().getLong(Keys.PROTOCOL_INTERVAL.withPrefix(protocol.getName()))); - accessId = Context.getConfig().getString(Keys.ORBCOMM_ACCESS_ID); - password = Context.getConfig().getString(Keys.ORBCOMM_PASSWORD); - host = Context.getConfig().getString(Keys.PROTOCOL_ADDRESS.withPrefix(protocol.getName())); + public OrbcommProtocolPoller(Protocol protocol, Config config) { + super(config.getLong(Keys.PROTOCOL_INTERVAL.withPrefix(protocol.getName()))); + accessId = config.getString(Keys.ORBCOMM_ACCESS_ID); + password = config.getString(Keys.ORBCOMM_PASSWORD); + host = config.getString(Keys.PROTOCOL_ADDRESS.withPrefix(protocol.getName())); } @Override diff --git a/src/main/java/org/traccar/protocol/OrionProtocol.java b/src/main/java/org/traccar/protocol/OrionProtocol.java index 8485ae638..b78af462b 100644 --- a/src/main/java/org/traccar/protocol/OrionProtocol.java +++ b/src/main/java/org/traccar/protocol/OrionProtocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class OrionProtocol extends BaseProtocol { - public OrionProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public OrionProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new OrionFrameDecoder()); pipeline.addLast(new OrionProtocolDecoder(OrionProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/OrionProtocolDecoder.java b/src/main/java/org/traccar/protocol/OrionProtocolDecoder.java index af819989e..681891edb 100644 --- a/src/main/java/org/traccar/protocol/OrionProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/OrionProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; diff --git a/src/main/java/org/traccar/protocol/OsmAndProtocol.java b/src/main/java/org/traccar/protocol/OsmAndProtocol.java index d3aa2fd6f..e06580949 100644 --- a/src/main/java/org/traccar/protocol/OsmAndProtocol.java +++ b/src/main/java/org/traccar/protocol/OsmAndProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class OsmAndProtocol extends BaseProtocol { - public OsmAndProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public OsmAndProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new HttpResponseEncoder()); pipeline.addLast(new HttpRequestDecoder()); pipeline.addLast(new HttpObjectAggregator(16384)); diff --git a/src/main/java/org/traccar/protocol/OsmAndProtocolDecoder.java b/src/main/java/org/traccar/protocol/OsmAndProtocolDecoder.java index ec9bbc240..c8968023a 100644 --- a/src/main/java/org/traccar/protocol/OsmAndProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/OsmAndProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 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. @@ -21,10 +21,8 @@ import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.QueryStringDecoder; import org.traccar.BaseHttpProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; -import org.traccar.database.CommandsManager; import org.traccar.helper.DateUtil; import org.traccar.model.CellTower; import org.traccar.model.Command; @@ -61,6 +59,8 @@ public class OsmAndProtocolDecoder extends BaseHttpProtocolDecoder { position.setValid(true); Network network = new Network(); + Double latitude = null; + Double longitude = null; for (Map.Entry<String, List<String>> entry : params.entrySet()) { for (String value : entry.getValue()) { @@ -94,15 +94,15 @@ public class OsmAndProtocolDecoder extends BaseHttpProtocolDecoder { } break; case "lat": - position.setLatitude(Double.parseDouble(value)); + latitude = Double.parseDouble(value); break; case "lon": - position.setLongitude(Double.parseDouble(value)); + longitude = Double.parseDouble(value); break; case "location": String[] location = value.split(","); - position.setLatitude(Double.parseDouble(location[0])); - position.setLongitude(Double.parseDouble(location[1])); + latitude = Double.parseDouble(location[0]); + longitude = Double.parseDouble(location[1]); break; case "cell": String[] cell = value.split(","); @@ -143,6 +143,9 @@ public class OsmAndProtocolDecoder extends BaseHttpProtocolDecoder { case "driverUniqueId": position.set(Position.KEY_DRIVER_UNIQUE_ID, value); break; + case "charge": + position.set(Position.KEY_CHARGE, Boolean.parseBoolean(value)); + break; default: try { position.set(entry.getKey(), Double.parseDouble(value)); @@ -172,17 +175,17 @@ public class OsmAndProtocolDecoder extends BaseHttpProtocolDecoder { position.setNetwork(network); } - if (position.getLatitude() == 0 && position.getLongitude() == 0) { + if (latitude != null && longitude != null) { + position.setLatitude(latitude); + position.setLongitude(longitude); + } else { getLastLocation(position, position.getDeviceTime()); } if (position.getDeviceId() != 0) { String response = null; - CommandsManager commandsManager = Context.getCommandsManager(); - if (commandsManager != null) { - for (Command command : commandsManager.readQueuedCommands(position.getDeviceId(), 1)) { - response = command.getString(Command.KEY_DATA); - } + for (Command command : getCommandsManager().readQueuedCommands(position.getDeviceId(), 1)) { + response = command.getString(Command.KEY_DATA); } if (response != null) { sendResponse(channel, HttpResponseStatus.OK, Unpooled.copiedBuffer(response, StandardCharsets.UTF_8)); diff --git a/src/main/java/org/traccar/protocol/OutsafeProtocol.java b/src/main/java/org/traccar/protocol/OutsafeProtocol.java index c728a404d..159534883 100644 --- a/src/main/java/org/traccar/protocol/OutsafeProtocol.java +++ b/src/main/java/org/traccar/protocol/OutsafeProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class OutsafeProtocol extends BaseProtocol { - public OutsafeProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public OutsafeProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new HttpResponseEncoder()); pipeline.addLast(new HttpRequestDecoder()); pipeline.addLast(new HttpObjectAggregator(65535)); diff --git a/src/main/java/org/traccar/protocol/OutsafeProtocolDecoder.java b/src/main/java/org/traccar/protocol/OutsafeProtocolDecoder.java index 9de77d241..f71778412 100644 --- a/src/main/java/org/traccar/protocol/OutsafeProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/OutsafeProtocolDecoder.java @@ -19,15 +19,15 @@ import io.netty.channel.Channel; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpResponseStatus; import org.traccar.BaseHttpProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.model.Position; -import javax.json.Json; -import javax.json.JsonNumber; -import javax.json.JsonObject; -import javax.json.JsonString; -import javax.json.JsonValue; +import jakarta.json.Json; +import jakarta.json.JsonNumber; +import jakarta.json.JsonObject; +import jakarta.json.JsonString; +import jakarta.json.JsonValue; import java.io.StringReader; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; diff --git a/src/main/java/org/traccar/protocol/OwnTracksProtocol.java b/src/main/java/org/traccar/protocol/OwnTracksProtocol.java index 0086371d8..c509ad282 100644 --- a/src/main/java/org/traccar/protocol/OwnTracksProtocol.java +++ b/src/main/java/org/traccar/protocol/OwnTracksProtocol.java @@ -22,13 +22,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class OwnTracksProtocol extends BaseProtocol { - public OwnTracksProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public OwnTracksProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new HttpResponseEncoder()); pipeline.addLast(new HttpRequestDecoder()); pipeline.addLast(new HttpObjectAggregator(16384)); diff --git a/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java b/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java index 509d14ae4..e54d07fa7 100644 --- a/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java @@ -20,13 +20,13 @@ import io.netty.channel.Channel; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpResponseStatus; import org.traccar.BaseHttpProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; -import javax.json.Json; -import javax.json.JsonObject; +import jakarta.json.Json; +import jakarta.json.JsonObject; import java.io.StringReader; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; diff --git a/src/main/java/org/traccar/protocol/PacificTrackProtocol.java b/src/main/java/org/traccar/protocol/PacificTrackProtocol.java index 08991ab64..a315d4d9f 100644 --- a/src/main/java/org/traccar/protocol/PacificTrackProtocol.java +++ b/src/main/java/org/traccar/protocol/PacificTrackProtocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class PacificTrackProtocol extends BaseProtocol { - public PacificTrackProtocol() { - addServer(new TrackerServer(true, getName()) { + @Inject + public PacificTrackProtocol(Config config) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new PacificTrackProtocolDecoder(PacificTrackProtocol.this)); } }); diff --git a/src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java index b5d34a029..7079745be 100644 --- a/src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.BitUtil; import org.traccar.helper.DateBuilder; diff --git a/src/main/java/org/traccar/protocol/PathAwayProtocol.java b/src/main/java/org/traccar/protocol/PathAwayProtocol.java index 6b5d75c5e..a65740475 100644 --- a/src/main/java/org/traccar/protocol/PathAwayProtocol.java +++ b/src/main/java/org/traccar/protocol/PathAwayProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class PathAwayProtocol extends BaseProtocol { - public PathAwayProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public PathAwayProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new HttpResponseEncoder()); pipeline.addLast(new HttpRequestDecoder()); pipeline.addLast(new HttpObjectAggregator(16384)); diff --git a/src/main/java/org/traccar/protocol/PathAwayProtocolDecoder.java b/src/main/java/org/traccar/protocol/PathAwayProtocolDecoder.java index 02a15e34a..3e7fa9a5b 100644 --- a/src/main/java/org/traccar/protocol/PathAwayProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/PathAwayProtocolDecoder.java @@ -24,7 +24,7 @@ import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.QueryStringDecoder; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/PiligrimProtocol.java b/src/main/java/org/traccar/protocol/PiligrimProtocol.java index d88c1ab72..9dd1bc491 100644 --- a/src/main/java/org/traccar/protocol/PiligrimProtocol.java +++ b/src/main/java/org/traccar/protocol/PiligrimProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class PiligrimProtocol extends BaseProtocol { - public PiligrimProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public PiligrimProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new HttpResponseEncoder()); pipeline.addLast(new HttpRequestDecoder()); pipeline.addLast(new HttpObjectAggregator(16384)); diff --git a/src/main/java/org/traccar/protocol/PiligrimProtocolDecoder.java b/src/main/java/org/traccar/protocol/PiligrimProtocolDecoder.java index 26ce2fe53..34c879cb8 100644 --- a/src/main/java/org/traccar/protocol/PiligrimProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/PiligrimProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2014 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,16 +22,19 @@ import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.QueryStringDecoder; import org.traccar.BaseHttpProtocolDecoder; -import org.traccar.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.BitUtil; import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; import org.traccar.model.Position; +import org.traccar.session.DeviceSession; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; import java.util.LinkedList; import java.util.List; +import java.util.regex.Pattern; public class PiligrimProtocolDecoder extends BaseHttpProtocolDecoder { @@ -47,6 +50,21 @@ public class PiligrimProtocolDecoder extends BaseHttpProtocolDecoder { public static final int MSG_GPS_SENSORS = 0xF2; public static final int MSG_EVENTS = 0xF3; + private static final Pattern PATTERN = new PatternBuilder() + .expression("[^$]+") + .text("$GPRMC,") + .number("(dd)(dd)(dd).d+,") // time (hhmmss) + .expression("([AV]),") // validity + .number("(dd)(dd.d+),") // latitude + .expression("([NS]),") + .number("(d{2,3})(dd.d+),") // longitude + .expression("([EW]),") + .number("(d+.d+),") // speed + .number("(d+.d+),") // course + .number("(dd)(dd)(dd),") // date (ddmmyy) + .any() + .compile(); + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { @@ -150,6 +168,42 @@ public class PiligrimProtocolDecoder extends BaseHttpProtocolDecoder { } return positions; + + } else if (uri.startsWith("/push.do")) { + + sendResponse(channel, "PUSH.DO: OK"); + + String sentence = request.content().toString(StandardCharsets.US_ASCII); + + String[] parts = sentence.split("&"); + String phone = parts[1].substring(16); + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, phone); + if (deviceSession == null) { + return null; + } + + Parser parser = new Parser(PATTERN, parts[2]); + if (!parser.matches()) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + DateBuilder dateBuilder = new DateBuilder() + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + + position.setValid(parser.next().equals("A")); + position.setLatitude(parser.nextCoordinate()); + position.setLongitude(parser.nextCoordinate()); + position.setSpeed(parser.nextDouble()); + position.setCourse(parser.nextDouble()); + + dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); + + return position; + } return null; diff --git a/src/main/java/org/traccar/protocol/PluginProtocol.java b/src/main/java/org/traccar/protocol/PluginProtocol.java index d5f28da9d..fff1830e8 100644 --- a/src/main/java/org/traccar/protocol/PluginProtocol.java +++ b/src/main/java/org/traccar/protocol/PluginProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class PluginProtocol extends BaseProtocol { - public PluginProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public PluginProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '#')); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/PluginProtocolDecoder.java b/src/main/java/org/traccar/protocol/PluginProtocolDecoder.java index 65de211ac..6ee95d18a 100644 --- a/src/main/java/org/traccar/protocol/PluginProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/PluginProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/PolteProtocol.java b/src/main/java/org/traccar/protocol/PolteProtocol.java index a3e548716..0fbedfb09 100644 --- a/src/main/java/org/traccar/protocol/PolteProtocol.java +++ b/src/main/java/org/traccar/protocol/PolteProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class PolteProtocol extends BaseProtocol { - public PolteProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public PolteProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new HttpResponseEncoder()); pipeline.addLast(new HttpRequestDecoder()); pipeline.addLast(new HttpObjectAggregator(65535)); diff --git a/src/main/java/org/traccar/protocol/PolteProtocolDecoder.java b/src/main/java/org/traccar/protocol/PolteProtocolDecoder.java index ce45abef6..8954db491 100644 --- a/src/main/java/org/traccar/protocol/PolteProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/PolteProtocolDecoder.java @@ -19,12 +19,12 @@ import io.netty.channel.Channel; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpResponseStatus; import org.traccar.BaseHttpProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.model.Position; -import javax.json.Json; -import javax.json.JsonObject; +import jakarta.json.Json; +import jakarta.json.JsonObject; import java.io.StringReader; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; diff --git a/src/main/java/org/traccar/protocol/PortmanProtocol.java b/src/main/java/org/traccar/protocol/PortmanProtocol.java index b7faae08a..3a4b49289 100644 --- a/src/main/java/org/traccar/protocol/PortmanProtocol.java +++ b/src/main/java/org/traccar/protocol/PortmanProtocol.java @@ -21,17 +21,21 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class PortmanProtocol extends BaseProtocol { - public PortmanProtocol() { + @Inject + public PortmanProtocol(Config config) { setSupportedDataCommands( Command.TYPE_ENGINE_STOP, Command.TYPE_ENGINE_RESUME); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/PortmanProtocolDecoder.java b/src/main/java/org/traccar/protocol/PortmanProtocolDecoder.java index e1847a2b2..da9403313 100644 --- a/src/main/java/org/traccar/protocol/PortmanProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/PortmanProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/PretraceProtocol.java b/src/main/java/org/traccar/protocol/PretraceProtocol.java index 9d35c1c2f..54a34fc69 100644 --- a/src/main/java/org/traccar/protocol/PretraceProtocol.java +++ b/src/main/java/org/traccar/protocol/PretraceProtocol.java @@ -21,17 +21,21 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class PretraceProtocol extends BaseProtocol { - public PretraceProtocol() { + @Inject + public PretraceProtocol(Config config) { setSupportedDataCommands( Command.TYPE_CUSTOM, Command.TYPE_POSITION_PERIODIC); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ')')); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/PretraceProtocolDecoder.java b/src/main/java/org/traccar/protocol/PretraceProtocolDecoder.java index a19384e62..ff6ad763a 100644 --- a/src/main/java/org/traccar/protocol/PretraceProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/PretraceProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/PretraceProtocolEncoder.java b/src/main/java/org/traccar/protocol/PretraceProtocolEncoder.java index 1083a252e..a109e7a07 100644 --- a/src/main/java/org/traccar/protocol/PretraceProtocolEncoder.java +++ b/src/main/java/org/traccar/protocol/PretraceProtocolEncoder.java @@ -16,10 +16,9 @@ package org.traccar.protocol; import org.traccar.BaseProtocolEncoder; -import org.traccar.Context; +import org.traccar.Protocol; import org.traccar.helper.Checksum; import org.traccar.model.Command; -import org.traccar.Protocol; public class PretraceProtocolEncoder extends BaseProtocolEncoder { @@ -35,7 +34,7 @@ public class PretraceProtocolEncoder extends BaseProtocolEncoder { @Override protected Object encodeCommand(Command command) { - String uniqueId = Context.getIdentityManager().getById(command.getDeviceId()).getUniqueId(); + String uniqueId = getUniqueId(command.getDeviceId()); switch (command.getType()) { case Command.TYPE_CUSTOM: diff --git a/src/main/java/org/traccar/protocol/PricolProtocol.java b/src/main/java/org/traccar/protocol/PricolProtocol.java index 6821cd949..7b0e7386c 100644 --- a/src/main/java/org/traccar/protocol/PricolProtocol.java +++ b/src/main/java/org/traccar/protocol/PricolProtocol.java @@ -19,20 +19,24 @@ import io.netty.handler.codec.FixedLengthFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class PricolProtocol extends BaseProtocol { - public PricolProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public PricolProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new FixedLengthFrameDecoder(64)); pipeline.addLast(new PricolProtocolDecoder(PricolProtocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new PricolProtocolDecoder(PricolProtocol.this)); } }); diff --git a/src/main/java/org/traccar/protocol/PricolProtocolDecoder.java b/src/main/java/org/traccar/protocol/PricolProtocolDecoder.java index 190c68258..5f6805f09 100644 --- a/src/main/java/org/traccar/protocol/PricolProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/PricolProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; diff --git a/src/main/java/org/traccar/protocol/ProgressProtocol.java b/src/main/java/org/traccar/protocol/ProgressProtocol.java index aac84205d..8d159ef24 100644 --- a/src/main/java/org/traccar/protocol/ProgressProtocol.java +++ b/src/main/java/org/traccar/protocol/ProgressProtocol.java @@ -19,14 +19,18 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import java.nio.ByteOrder; +import jakarta.inject.Inject; + public class ProgressProtocol extends BaseProtocol { - public ProgressProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public ProgressProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 2, 2, 4, 0, true)); pipeline.addLast(new ProgressProtocolDecoder(ProgressProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/ProgressProtocolDecoder.java b/src/main/java/org/traccar/protocol/ProgressProtocolDecoder.java index 0025cd9e7..e3a5881da 100644 --- a/src/main/java/org/traccar/protocol/ProgressProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/ProgressProtocolDecoder.java @@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/PstProtocol.java b/src/main/java/org/traccar/protocol/PstProtocol.java index d8c7008cb..01a83b31f 100644 --- a/src/main/java/org/traccar/protocol/PstProtocol.java +++ b/src/main/java/org/traccar/protocol/PstProtocol.java @@ -18,24 +18,28 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class PstProtocol extends BaseProtocol { - public PstProtocol() { + @Inject + public PstProtocol(Config config) { setSupportedDataCommands( Command.TYPE_ENGINE_STOP, Command.TYPE_ENGINE_RESUME); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new PstProtocolEncoder(PstProtocol.this)); pipeline.addLast(new PstProtocolDecoder(PstProtocol.this)); } }); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new PstFrameEncoder()); pipeline.addLast(new PstFrameDecoder()); pipeline.addLast(new PstProtocolEncoder(PstProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/PstProtocolDecoder.java b/src/main/java/org/traccar/protocol/PstProtocolDecoder.java index e3fe1af62..872e77a3a 100644 --- a/src/main/java/org/traccar/protocol/PstProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/PstProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/Pt215Protocol.java b/src/main/java/org/traccar/protocol/Pt215Protocol.java index 09bd9b121..fd67b1241 100644 --- a/src/main/java/org/traccar/protocol/Pt215Protocol.java +++ b/src/main/java/org/traccar/protocol/Pt215Protocol.java @@ -19,13 +19,17 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class Pt215Protocol extends BaseProtocol { - public Pt215Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public Pt215Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 2, 1, -1, 0)); pipeline.addLast(new Pt215ProtocolDecoder(Pt215Protocol.this)); } diff --git a/src/main/java/org/traccar/protocol/Pt215ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Pt215ProtocolDecoder.java index 48ce7dede..f669c5ffd 100644 --- a/src/main/java/org/traccar/protocol/Pt215ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Pt215ProtocolDecoder.java @@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/Pt3000Protocol.java b/src/main/java/org/traccar/protocol/Pt3000Protocol.java index 1ad0026a3..5f49084ed 100644 --- a/src/main/java/org/traccar/protocol/Pt3000Protocol.java +++ b/src/main/java/org/traccar/protocol/Pt3000Protocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class Pt3000Protocol extends BaseProtocol { - public Pt3000Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public Pt3000Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, 'd')); // probably wrong pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/Pt3000ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Pt3000ProtocolDecoder.java index e7f9e062a..c33660f51 100644 --- a/src/main/java/org/traccar/protocol/Pt3000ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Pt3000ProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/Pt502Protocol.java b/src/main/java/org/traccar/protocol/Pt502Protocol.java index 56444fb42..0257dd60f 100644 --- a/src/main/java/org/traccar/protocol/Pt502Protocol.java +++ b/src/main/java/org/traccar/protocol/Pt502Protocol.java @@ -19,20 +19,24 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class Pt502Protocol extends BaseProtocol { - public Pt502Protocol() { + @Inject + public Pt502Protocol(Config config) { setSupportedDataCommands( Command.TYPE_CUSTOM, Command.TYPE_SET_TIMEZONE, Command.TYPE_ALARM_SPEED, Command.TYPE_OUTPUT_CONTROL, Command.TYPE_REQUEST_PHOTO); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new Pt502FrameDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new Pt502ProtocolEncoder(Pt502Protocol.this)); diff --git a/src/main/java/org/traccar/protocol/Pt502ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Pt502ProtocolDecoder.java index ff92b51f1..2a6a81a65 100644 --- a/src/main/java/org/traccar/protocol/Pt502ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Pt502ProtocolDecoder.java @@ -20,8 +20,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; @@ -175,14 +174,13 @@ public class Pt502ProtocolDecoder extends BaseProtocolDecoder { } else { DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); - String uniqueId = Context.getIdentityManager().getById(deviceSession.getDeviceId()).getUniqueId(); Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); getLastLocation(position, null); - position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(uniqueId, photo, "jpg")); + position.set(Position.KEY_IMAGE, writeMediaFile(deviceSession.getUniqueId(), photo, "jpg")); photo.release(); photo = null; diff --git a/src/main/java/org/traccar/protocol/Pt60Protocol.java b/src/main/java/org/traccar/protocol/Pt60Protocol.java index c502426c5..83e3bfbeb 100644 --- a/src/main/java/org/traccar/protocol/Pt60Protocol.java +++ b/src/main/java/org/traccar/protocol/Pt60Protocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class Pt60Protocol extends BaseProtocol { - public Pt60Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public Pt60Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "@R#@", "@E#@")); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/Pt60ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Pt60ProtocolDecoder.java index 6a3fe2734..94b549fe6 100644 --- a/src/main/java/org/traccar/protocol/Pt60ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Pt60ProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/R12wProtocol.java b/src/main/java/org/traccar/protocol/R12wProtocol.java index 3726233b4..b5b3eff81 100644 --- a/src/main/java/org/traccar/protocol/R12wProtocol.java +++ b/src/main/java/org/traccar/protocol/R12wProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class R12wProtocol extends BaseProtocol { - public R12wProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public R12wProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/R12wProtocolDecoder.java b/src/main/java/org/traccar/protocol/R12wProtocolDecoder.java index d60318447..3be784911 100644 --- a/src/main/java/org/traccar/protocol/R12wProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/R12wProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.Checksum; diff --git a/src/main/java/org/traccar/protocol/RaceDynamicsProtocol.java b/src/main/java/org/traccar/protocol/RaceDynamicsProtocol.java index c9db10610..6f7340902 100644 --- a/src/main/java/org/traccar/protocol/RaceDynamicsProtocol.java +++ b/src/main/java/org/traccar/protocol/RaceDynamicsProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class RaceDynamicsProtocol extends BaseProtocol { - public RaceDynamicsProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public RaceDynamicsProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1500)); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/RaceDynamicsProtocolDecoder.java b/src/main/java/org/traccar/protocol/RaceDynamicsProtocolDecoder.java index f441bf8ed..89639ad30 100644 --- a/src/main/java/org/traccar/protocol/RaceDynamicsProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/RaceDynamicsProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; diff --git a/src/main/java/org/traccar/protocol/RadarProtocol.java b/src/main/java/org/traccar/protocol/RadarProtocol.java index 9783778f0..8985e0e83 100644 --- a/src/main/java/org/traccar/protocol/RadarProtocol.java +++ b/src/main/java/org/traccar/protocol/RadarProtocol.java @@ -19,13 +19,17 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class RadarProtocol extends BaseProtocol { - public RadarProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public RadarProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 12, 2, -14, 0)); pipeline.addLast(new RadarProtocolDecoder(RadarProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/RadarProtocolDecoder.java b/src/main/java/org/traccar/protocol/RadarProtocolDecoder.java index d87f77b84..818e97f8b 100644 --- a/src/main/java/org/traccar/protocol/RadarProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/RadarProtocolDecoder.java @@ -18,7 +18,7 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.BitUtil; import org.traccar.helper.UnitsConverter; diff --git a/src/main/java/org/traccar/protocol/RaveonProtocol.java b/src/main/java/org/traccar/protocol/RaveonProtocol.java index 44faadb3b..aa1a79219 100644 --- a/src/main/java/org/traccar/protocol/RaveonProtocol.java +++ b/src/main/java/org/traccar/protocol/RaveonProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class RaveonProtocol extends BaseProtocol { - public RaveonProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public RaveonProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/RaveonProtocolDecoder.java b/src/main/java/org/traccar/protocol/RaveonProtocolDecoder.java index 50acd20a1..dfc21bf69 100644 --- a/src/main/java/org/traccar/protocol/RaveonProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/RaveonProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/RecodaProtocol.java b/src/main/java/org/traccar/protocol/RecodaProtocol.java index 0bc9870bc..7d2fadae4 100644 --- a/src/main/java/org/traccar/protocol/RecodaProtocol.java +++ b/src/main/java/org/traccar/protocol/RecodaProtocol.java @@ -19,14 +19,18 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import java.nio.ByteOrder; +import jakarta.inject.Inject; + public class RecodaProtocol extends BaseProtocol { - public RecodaProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public RecodaProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 4, 4, -8, 0, true)); pipeline.addLast(new RecodaProtocolDecoder(RecodaProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/RecodaProtocolDecoder.java b/src/main/java/org/traccar/protocol/RecodaProtocolDecoder.java index 04098225f..0c417a62f 100644 --- a/src/main/java/org/traccar/protocol/RecodaProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/RecodaProtocolDecoder.java @@ -18,7 +18,7 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.BitUtil; import org.traccar.helper.UnitsConverter; diff --git a/src/main/java/org/traccar/protocol/RetranslatorProtocol.java b/src/main/java/org/traccar/protocol/RetranslatorProtocol.java index fae81f7d2..a349a8191 100644 --- a/src/main/java/org/traccar/protocol/RetranslatorProtocol.java +++ b/src/main/java/org/traccar/protocol/RetranslatorProtocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class RetranslatorProtocol extends BaseProtocol { - public RetranslatorProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public RetranslatorProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new RetranslatorFrameDecoder()); pipeline.addLast(new RetranslatorProtocolDecoder(RetranslatorProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/RetranslatorProtocolDecoder.java b/src/main/java/org/traccar/protocol/RetranslatorProtocolDecoder.java index 5bf6cef50..afbf7e511 100644 --- a/src/main/java/org/traccar/protocol/RetranslatorProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/RetranslatorProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.model.Position; diff --git a/src/main/java/org/traccar/protocol/RfTrackProtocol.java b/src/main/java/org/traccar/protocol/RfTrackProtocol.java new file mode 100644 index 000000000..ac033c348 --- /dev/null +++ b/src/main/java/org/traccar/protocol/RfTrackProtocol.java @@ -0,0 +1,43 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpRequestDecoder; +import io.netty.handler.codec.http.HttpResponseEncoder; +import org.traccar.BaseProtocol; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; + +public class RfTrackProtocol extends BaseProtocol { + + @Inject + public RfTrackProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(new HttpResponseEncoder()); + pipeline.addLast(new HttpRequestDecoder()); + pipeline.addLast(new HttpObjectAggregator(16384)); + pipeline.addLast(new RfTrackProtocolDecoder(RfTrackProtocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/RfTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/RfTrackProtocolDecoder.java new file mode 100644 index 000000000..cbb204e3b --- /dev/null +++ b/src/main/java/org/traccar/protocol/RfTrackProtocolDecoder.java @@ -0,0 +1,163 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.QueryStringDecoder; +import org.traccar.BaseHttpProtocolDecoder; +import org.traccar.Protocol; +import org.traccar.model.CellTower; +import org.traccar.model.Command; +import org.traccar.model.Network; +import org.traccar.model.Position; +import org.traccar.model.WifiAccessPoint; +import org.traccar.session.DeviceSession; + +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import java.io.StringReader; +import java.net.SocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.List; +import java.util.Map; + +public class RfTrackProtocolDecoder extends BaseHttpProtocolDecoder { + + public RfTrackProtocolDecoder(Protocol protocol) { + super(protocol); + } + + @Override + protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + FullHttpRequest request = (FullHttpRequest) msg; + QueryStringDecoder decoder = new QueryStringDecoder( + request.content().toString(StandardCharsets.US_ASCII), false); + Map<String, List<String>> params = decoder.parameters(); + + Position position = new Position(getProtocolName()); + Network network = new Network(); + + for (Map.Entry<String, List<String>> entry : params.entrySet()) { + for (String value : entry.getValue()) { + switch (entry.getKey()) { + case "i": + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, value); + if (deviceSession == null) { + sendResponse(channel, HttpResponseStatus.BAD_REQUEST); + return null; + } + position.setDeviceId(deviceSession.getDeviceId()); + break; + case "v": + position.set(Position.KEY_VERSION_FW, value); + break; + case "t": + position.setDeviceTime(new Date(Long.parseLong(value))); + break; + case "bat": + int battery = Integer.parseInt(value); + position.set(Position.KEY_BATTERY_LEVEL, battery & 0xff); + position.set("plugStatus", (battery >> 8) & 0x0f); + position.set(Position.KEY_CHARGE, ((battery >> 12) & 0x0f) == 1); + break; + case "id": + position.set("braceletId", value); + break; + case "rc": + int braceletCode = Integer.parseInt(value); + position.set("braceletCode", braceletCode & 0xffff); + position.set("braceletStatus", braceletCode >> 16); + break; + case "idt": + long braceletTime = Long.parseLong(value); + position.set("lastHeartbeat", (braceletTime >> 45) * 10); + position.set("lastPaired", ((braceletTime >> 30) & 0xffff) * 10); + position.set("lastUnpaired", ((braceletTime >> 15) & 0xffff) * 10); + break; + case "mt": + int vibrationTime = Integer.parseInt(value); + position.set("vibrationDevice", (vibrationTime & 0x7fff) * 10); + position.set("vibrationBracelet", (vibrationTime >> 15) * 10); + break; + case "gps": + JsonObject location = Json.createReader(new StringReader(value)).readObject(); + position.setValid(true); + position.setAccuracy(location.getJsonNumber("a").doubleValue()); + position.setLongitude(location.getJsonNumber("x").doubleValue()); + position.setLatitude(location.getJsonNumber("y").doubleValue()); + position.setAltitude(location.getJsonNumber("z").doubleValue()); + position.setFixTime(new Date(location.getJsonNumber("t").longValue())); + break; + case "gsm": + JsonObject cellInfo = Json.createReader(new StringReader(value)).readObject(); + int mcc = cellInfo.getInt("c"); + int mnc = cellInfo.getInt("n"); + JsonArray cells = cellInfo.getJsonArray("b"); + for (int i = 0; i < cells.size(); i++) { + JsonObject cell = cells.getJsonObject(i); + network.addCellTower(CellTower.from( + mcc, mnc, cell.getInt("l"), cell.getInt("c"), cell.getInt("b"))); + } + break; + case "dbm": + position.set(Position.KEY_RSSI, Integer.parseInt(value)); + break; + case "bar": + position.set("pressure", Double.parseDouble(value)); + break; + case "cob": + position.set("pressureChanges", value); + break; + case "wifi": + JsonArray wifiInfo = Json.createReader(new StringReader(value)).readArray(); + for (int i = 0; i < wifiInfo.size(); i++) { + JsonObject wifi = wifiInfo.getJsonObject(i); + network.addWifiAccessPoint(WifiAccessPoint.from( + wifi.getString("m").replace('-', ':'), wifi.getInt("l"))); + } + break; + case "u_ids": + position.set("unpairedIds", value); + break; + default: + break; + } + } + } + + if (position.getFixTime() == null) { + getLastLocation(position, position.getDeviceTime()); + } + + if (network.getCellTowers() != null || network.getWifiAccessPoints() != null) { + position.setNetwork(network); + } + + String response = "{}"; + for (Command command : getCommandsManager().readQueuedCommands(position.getDeviceId(), 1)) { + response = command.getString(Command.KEY_DATA); + } + sendResponse(channel, HttpResponseStatus.OK, Unpooled.copiedBuffer(response, StandardCharsets.UTF_8)); + return position; + } + +} diff --git a/src/main/java/org/traccar/protocol/RitiProtocol.java b/src/main/java/org/traccar/protocol/RitiProtocol.java index de1026672..9916042a8 100644 --- a/src/main/java/org/traccar/protocol/RitiProtocol.java +++ b/src/main/java/org/traccar/protocol/RitiProtocol.java @@ -19,14 +19,18 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import java.nio.ByteOrder; +import jakarta.inject.Inject; + public class RitiProtocol extends BaseProtocol { - public RitiProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public RitiProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 105, 2, 3, 0, true)); pipeline.addLast(new RitiProtocolDecoder(RitiProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/RitiProtocolDecoder.java b/src/main/java/org/traccar/protocol/RitiProtocolDecoder.java index 46267ca90..501d5faa7 100644 --- a/src/main/java/org/traccar/protocol/RitiProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/RitiProtocolDecoder.java @@ -18,7 +18,7 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/RoboTrackProtocol.java b/src/main/java/org/traccar/protocol/RoboTrackProtocol.java index c2c531293..229c343bb 100644 --- a/src/main/java/org/traccar/protocol/RoboTrackProtocol.java +++ b/src/main/java/org/traccar/protocol/RoboTrackProtocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class RoboTrackProtocol extends BaseProtocol { - public RoboTrackProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public RoboTrackProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new RoboTrackFrameDecoder()); pipeline.addLast(new RoboTrackProtocolDecoder(RoboTrackProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/RoboTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/RoboTrackProtocolDecoder.java index b613f31d7..ffe16bd7b 100644 --- a/src/main/java/org/traccar/protocol/RoboTrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/RoboTrackProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/RstProtocol.java b/src/main/java/org/traccar/protocol/RstProtocol.java index 10d11d493..0bb809a49 100644 --- a/src/main/java/org/traccar/protocol/RstProtocol.java +++ b/src/main/java/org/traccar/protocol/RstProtocol.java @@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class RstProtocol extends BaseProtocol { - public RstProtocol() { - addServer(new TrackerServer(true, getName()) { + @Inject + public RstProtocol(Config config) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new RstProtocolDecoder(RstProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/RstProtocolDecoder.java b/src/main/java/org/traccar/protocol/RstProtocolDecoder.java index 9e3261a04..fcc96fbf1 100644 --- a/src/main/java/org/traccar/protocol/RstProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/RstProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/RuptelaProtocol.java b/src/main/java/org/traccar/protocol/RuptelaProtocol.java index 5d1f86553..9f399e299 100644 --- a/src/main/java/org/traccar/protocol/RuptelaProtocol.java +++ b/src/main/java/org/traccar/protocol/RuptelaProtocol.java @@ -19,11 +19,15 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class RuptelaProtocol extends BaseProtocol { - public RuptelaProtocol() { + @Inject + public RuptelaProtocol(Config config) { setSupportedDataCommands( Command.TYPE_CUSTOM, Command.TYPE_ENGINE_STOP, @@ -35,9 +39,9 @@ public class RuptelaProtocol extends BaseProtocol { Command.TYPE_OUTPUT_CONTROL, Command.TYPE_SET_CONNECTION, Command.TYPE_SET_ODOMETER); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 0, 2, 2, 0)); pipeline.addLast(new RuptelaProtocolEncoder(RuptelaProtocol.this)); pipeline.addLast(new RuptelaProtocolDecoder(RuptelaProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java b/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java index 2812d22ff..649de7c5c 100644 --- a/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 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. @@ -19,10 +19,10 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; +import org.traccar.helper.BitUtil; import org.traccar.helper.DataConverter; import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; @@ -50,6 +50,7 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_SMS_VIA_GPRS_RESPONSE = 7; public static final int MSG_SMS_VIA_GPRS = 8; public static final int MSG_DTCS = 9; + public static final int MSG_IDENTIFICATION = 15; public static final int MSG_SET_IO = 17; public static final int MSG_FILES = 37; public static final int MSG_EXTENDED_RECORDS = 68; @@ -92,22 +93,48 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder { } } + private void decodeDriver(Position position, String part1, String part2) { + Long driverIdPart1 = (Long) position.getAttributes().remove(part1); + Long driverIdPart2 = (Long) position.getAttributes().remove(part2); + if (driverIdPart1 != null && driverIdPart2 != null) { + ByteBuf driverId = Unpooled.copyLong(driverIdPart1, driverIdPart2); + position.set(Position.KEY_DRIVER_UNIQUE_ID, driverId.toString(StandardCharsets.US_ASCII)); + driverId.release(); + } + } + private void decodeParameter(Position position, int id, ByteBuf buf, int length) { switch (id) { case 2: case 3: case 4: - position.set("di" + (id - 1), readValue(buf, length, false)); - break; case 5: - position.set(Position.KEY_IGNITION, readValue(buf, length, false) == 1); + position.set(Position.PREFIX_IN + (id - 1), readValue(buf, length, false)); + break; + case 20: + position.set(Position.PREFIX_ADC + 3, readValue(buf, length, false)); + break; + case 21: + position.set(Position.PREFIX_ADC + 4, readValue(buf, length, false)); + break; + case 22: + position.set(Position.PREFIX_ADC + 1, readValue(buf, length, false)); + break; + case 23: + position.set(Position.PREFIX_ADC + 2, readValue(buf, length, false)); break; case 29: - position.set(Position.KEY_POWER, readValue(buf, length, false)); + position.set(Position.KEY_POWER, readValue(buf, length, false) * 0.001); break; case 30: position.set(Position.KEY_BATTERY, readValue(buf, length, false) * 0.001); break; + case 32: + position.set(Position.KEY_DEVICE_TEMP, readValue(buf, length, true)); + break; + case 65: + position.set(Position.KEY_ODOMETER, readValue(buf, length, true)); + break; case 74: position.set(Position.PREFIX_TEMP + 3, readValue(buf, length, true) * 0.1); break; @@ -116,6 +143,14 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder { case 80: position.set(Position.PREFIX_TEMP + (id - 78), readValue(buf, length, true) * 0.1); break; + case 88: + if (readValue(buf, length, false) > 0) { + position.set(Position.KEY_ALARM, Position.ALARM_JAMMING); + } + break; + case 95: + position.set(Position.KEY_OBD_SPEED, UnitsConverter.knotsFromKph(readValue(buf, length, true))); + break; case 134: if (readValue(buf, length, false) > 0) { position.set(Position.KEY_ALARM, Position.ALARM_BRAKING); @@ -126,9 +161,45 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION); } break; + case 150: + position.set(Position.KEY_OPERATOR, readValue(buf, length, false)); + break; + case 170: + position.set(Position.KEY_CHARGE, readValue(buf, length, false) > 0); + break; + case 173: + position.set(Position.KEY_MOTION, readValue(buf, length, false) > 0); + break; case 197: position.set(Position.KEY_RPM, readValue(buf, length, false) * 0.125); break; + case 251: + case 409: + position.set(Position.KEY_IGNITION, readValue(buf, length, false) > 0); + break; + case 410: + if (readValue(buf, length, false) > 0) { + position.set(Position.KEY_ALARM, Position.ALARM_TOW); + } + break; + case 411: + if (readValue(buf, length, false) > 0) { + position.set(Position.KEY_ALARM, Position.ALARM_ACCIDENT); + } + break; + case 415: + if (readValue(buf, length, false) == 0) { + position.set(Position.KEY_ALARM, Position.ALARM_GPS_ANTENNA_CUT); + } + break; + case 645: + position.set(Position.KEY_OBD_ODOMETER, readValue(buf, length, true) * 1000); + break; + case 758: + if (readValue(buf, length, false) == 1) { + position.set(Position.KEY_ALARM, Position.ALARM_TAMPERING); + } + break; default: position.set(Position.PREFIX_IO + id, readValue(buf, length, false)); break; @@ -166,22 +237,32 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedByte(); // timestamp extension if (type == MSG_EXTENDED_RECORDS) { - buf.readUnsignedByte(); // record extension + int recordExtension = buf.readUnsignedByte(); + int mergeRecordCount = BitUtil.from(recordExtension, 4); + int currentRecord = BitUtil.to(recordExtension, 4); + + if (currentRecord > 0 && currentRecord <= mergeRecordCount) { + position = positions.remove(positions.size() - 1); + } } buf.readUnsignedByte(); // priority (reserved) - position.setValid(true); - position.setLongitude(buf.readInt() / 10000000.0); - position.setLatitude(buf.readInt() / 10000000.0); - position.setAltitude(buf.readUnsignedShort() / 10.0); - position.setCourse(buf.readUnsignedShort() / 100.0); - - position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); - - position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort())); - - position.set(Position.KEY_HDOP, buf.readUnsignedByte() / 10.0); + int longitude = buf.readInt(); + int latitude = buf.readInt(); + if (longitude > Integer.MIN_VALUE && latitude > Integer.MIN_VALUE) { + position.setValid(true); + position.setLongitude(longitude / 10000000.0); + position.setLatitude(latitude / 10000000.0); + position.setAltitude(buf.readUnsignedShort() / 10.0); + position.setCourse(buf.readUnsignedShort() / 100.0); + position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); + position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort())); + position.set(Position.KEY_HDOP, buf.readUnsignedByte() / 10.0); + } else { + buf.skipBytes(8); + getLastLocation(position, null); + } if (type == MSG_EXTENDED_RECORDS) { position.set(Position.KEY_EVENT, buf.readUnsignedShort()); @@ -217,12 +298,13 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder { decodeParameter(position, id, buf, 8); } - Long driverIdPart1 = (Long) position.getAttributes().remove(Position.PREFIX_IO + 126); - Long driverIdPart2 = (Long) position.getAttributes().remove(Position.PREFIX_IO + 127); - if (driverIdPart1 != null && driverIdPart2 != null) { - ByteBuf driverId = Unpooled.copyLong(driverIdPart1, driverIdPart2); - position.set(Position.KEY_DRIVER_UNIQUE_ID, driverId.toString(StandardCharsets.US_ASCII)); - driverId.release(); + decodeDriver(position, Position.PREFIX_IO + 126, Position.PREFIX_IO + 127); // can driver + decodeDriver(position, Position.PREFIX_IO + 155, Position.PREFIX_IO + 156); // tco driver + + Long tagIdPart1 = (Long) position.getAttributes().remove(Position.PREFIX_IO + 760); + Long tagIdPart2 = (Long) position.getAttributes().remove(Position.PREFIX_IO + 761); + if (tagIdPart1 != null && tagIdPart2 != null) { + position.set("tagId", Long.toHexString(tagIdPart1) + Long.toHexString(tagIdPart2)); } positions.add(position); @@ -297,7 +379,7 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder { Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); getLastLocation(position, null); - position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(imei, photo, "jpg")); + position.set(Position.KEY_IMAGE, writeMediaFile(imei, photo, "jpg")); photo.release(); photo = null; return position; @@ -306,6 +388,18 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder { return null; + } else if (type == MSG_IDENTIFICATION) { + + ByteBuf content = Unpooled.buffer(); + content.writeByte(1); + ByteBuf response = RuptelaProtocolEncoder.encodeContent(type, content); + content.release(); + if (channel != null) { + channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); + } + + return null; + } else { return decodeCommandResponse(deviceSession, type, buf); diff --git a/src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java b/src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java index 442961b19..5ec971a98 100644 --- a/src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java +++ b/src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java @@ -80,14 +80,14 @@ public class RuptelaProtocolEncoder extends BaseProtocolEncoder { return encodeContent(RuptelaProtocolDecoder.MSG_FIRMWARE_UPDATE, content); case Command.TYPE_OUTPUT_CONTROL: content.writeInt(command.getInteger(Command.KEY_INDEX)); - content.writeInt(Integer.parseInt(command.getString(Command.KEY_DATA))); + content.writeInt(command.getInteger(Command.KEY_DATA)); return encodeContent(RuptelaProtocolDecoder.MSG_SET_IO, content); case Command.TYPE_SET_CONNECTION: String c = command.getString(Command.KEY_SERVER) + "," + command.getInteger(Command.KEY_PORT) + ",TCP"; content.writeBytes(c.getBytes(StandardCharsets.US_ASCII)); return encodeContent(RuptelaProtocolDecoder.MSG_SET_CONNECTION, content); case Command.TYPE_SET_ODOMETER: - content.writeInt(Integer.parseInt(command.getString(Command.KEY_DATA))); + content.writeInt(command.getInteger(Command.KEY_DATA)); return encodeContent(RuptelaProtocolDecoder.MSG_SET_ODOMETER, content); default: return null; diff --git a/src/main/java/org/traccar/protocol/S168Protocol.java b/src/main/java/org/traccar/protocol/S168Protocol.java index e78664c40..5fb0c6e72 100644 --- a/src/main/java/org/traccar/protocol/S168Protocol.java +++ b/src/main/java/org/traccar/protocol/S168Protocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class S168Protocol extends BaseProtocol { - public S168Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public S168Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '$')); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/S168ProtocolDecoder.java b/src/main/java/org/traccar/protocol/S168ProtocolDecoder.java index 6d565517b..cf665c6ba 100644 --- a/src/main/java/org/traccar/protocol/S168ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/S168ProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.UnitsConverter; import org.traccar.model.CellTower; @@ -107,7 +107,7 @@ public class S168ProtocolDecoder extends BaseProtocolDecoder { if (network.getCellTowers() != null || network.getWifiAccessPoints() != null) { position.setNetwork(network); } - if (!position.getAttributes().containsKey(Position.KEY_SATELLITES)) { + if (!position.hasAttribute(Position.KEY_SATELLITES)) { getLastLocation(position, null); } diff --git a/src/main/java/org/traccar/protocol/SabertekProtocol.java b/src/main/java/org/traccar/protocol/SabertekProtocol.java index 0ec847b60..cb3f2ab32 100644 --- a/src/main/java/org/traccar/protocol/SabertekProtocol.java +++ b/src/main/java/org/traccar/protocol/SabertekProtocol.java @@ -19,13 +19,17 @@ import io.netty.handler.codec.string.StringDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class SabertekProtocol extends BaseProtocol { - public SabertekProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public SabertekProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new SabertekFrameDecoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new SabertekProtocolDecoder(SabertekProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/SabertekProtocolDecoder.java b/src/main/java/org/traccar/protocol/SabertekProtocolDecoder.java index 3033aa2cc..71279812c 100644 --- a/src/main/java/org/traccar/protocol/SabertekProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/SabertekProtocolDecoder.java @@ -18,7 +18,7 @@ package org.traccar.protocol; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/SanavProtocol.java b/src/main/java/org/traccar/protocol/SanavProtocol.java index 6799c57e6..ac1941725 100644 --- a/src/main/java/org/traccar/protocol/SanavProtocol.java +++ b/src/main/java/org/traccar/protocol/SanavProtocol.java @@ -20,21 +20,25 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class SanavProtocol extends BaseProtocol { - public SanavProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public SanavProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new SanavProtocolDecoder(SanavProtocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new SanavProtocolDecoder(SanavProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/SanavProtocolDecoder.java b/src/main/java/org/traccar/protocol/SanavProtocolDecoder.java index 7e1c158e6..6741cb67c 100644 --- a/src/main/java/org/traccar/protocol/SanavProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/SanavProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.BitUtil; import org.traccar.helper.DateBuilder; diff --git a/src/main/java/org/traccar/protocol/SanulProtocol.java b/src/main/java/org/traccar/protocol/SanulProtocol.java index 3104e9366..cba162296 100644 --- a/src/main/java/org/traccar/protocol/SanulProtocol.java +++ b/src/main/java/org/traccar/protocol/SanulProtocol.java @@ -19,15 +19,19 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import java.nio.ByteOrder; +import jakarta.inject.Inject; + public class SanulProtocol extends BaseProtocol { - public SanulProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public SanulProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 3, 2, 0, 0, true)); pipeline.addLast(new SanulProtocolDecoder(SanulProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/SanulProtocolDecoder.java b/src/main/java/org/traccar/protocol/SanulProtocolDecoder.java index 036d1ee51..9568cd6d3 100644 --- a/src/main/java/org/traccar/protocol/SanulProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/SanulProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.model.Position; diff --git a/src/main/java/org/traccar/protocol/SatsolProtocol.java b/src/main/java/org/traccar/protocol/SatsolProtocol.java index b69fdd1fe..7252f99f0 100644 --- a/src/main/java/org/traccar/protocol/SatsolProtocol.java +++ b/src/main/java/org/traccar/protocol/SatsolProtocol.java @@ -19,15 +19,19 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import java.nio.ByteOrder; +import jakarta.inject.Inject; + public class SatsolProtocol extends BaseProtocol { - public SatsolProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public SatsolProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1400, 8, 2, 0, 0, true)); pipeline.addLast(new SatsolProtocolDecoder(SatsolProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/SatsolProtocolDecoder.java b/src/main/java/org/traccar/protocol/SatsolProtocolDecoder.java index c457d5620..37a84be04 100644 --- a/src/main/java/org/traccar/protocol/SatsolProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/SatsolProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/SigfoxProtocol.java b/src/main/java/org/traccar/protocol/SigfoxProtocol.java index e2f2cbe1f..edd624727 100644 --- a/src/main/java/org/traccar/protocol/SigfoxProtocol.java +++ b/src/main/java/org/traccar/protocol/SigfoxProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class SigfoxProtocol extends BaseProtocol { - public SigfoxProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public SigfoxProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new HttpResponseEncoder()); pipeline.addLast(new HttpRequestDecoder()); pipeline.addLast(new HttpObjectAggregator(65535)); diff --git a/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java b/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java index ce577f392..1298112d1 100644 --- a/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java @@ -22,7 +22,8 @@ import io.netty.channel.Channel; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpResponseStatus; import org.traccar.BaseHttpProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.helper.BufferUtil; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.BitUtil; import org.traccar.helper.DataConverter; @@ -31,11 +32,11 @@ import org.traccar.model.Network; import org.traccar.model.Position; import org.traccar.model.WifiAccessPoint; -import javax.json.Json; -import javax.json.JsonNumber; -import javax.json.JsonObject; -import javax.json.JsonString; -import javax.json.JsonValue; +import jakarta.json.Json; +import jakarta.json.JsonNumber; +import jakarta.json.JsonObject; +import jakarta.json.JsonString; +import jakarta.json.JsonValue; import java.io.StringReader; import java.net.SocketAddress; import java.net.URLDecoder; @@ -159,18 +160,8 @@ public class SigfoxProtocolDecoder extends BaseHttpProtocolDecoder { if (event == 0x0f || event == 0x1f) { position.setValid(event >> 4 > 0); - - long value; - value = buf.readUnsignedInt(); - position.setLatitude(BitUtil.to(value, 31) * 0.000001); - if (BitUtil.check(value, 31)) { - position.setLatitude(-position.getLatitude()); - } - value = buf.readUnsignedInt(); - position.setLongitude(BitUtil.to(value, 31) * 0.000001); - if (BitUtil.check(value, 31)) { - position.setLongitude(-position.getLongitude()); - } + position.setLatitude(BufferUtil.readSignedMagnitudeInt(buf) * 0.000001); + position.setLongitude(BufferUtil.readSignedMagnitudeInt(buf) * 0.000001); position.set(Position.KEY_BATTERY, (int) buf.readUnsignedByte()); diff --git a/src/main/java/org/traccar/protocol/SiwiProtocol.java b/src/main/java/org/traccar/protocol/SiwiProtocol.java index 8963721c8..59b96bf72 100644 --- a/src/main/java/org/traccar/protocol/SiwiProtocol.java +++ b/src/main/java/org/traccar/protocol/SiwiProtocol.java @@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class SiwiProtocol extends BaseProtocol { - public SiwiProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public SiwiProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringDecoder()); pipeline.addLast(new SiwiProtocolDecoder(SiwiProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/SiwiProtocolDecoder.java b/src/main/java/org/traccar/protocol/SiwiProtocolDecoder.java index bf8bfab77..7ba501834 100644 --- a/src/main/java/org/traccar/protocol/SiwiProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/SiwiProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/SkypatrolProtocol.java b/src/main/java/org/traccar/protocol/SkypatrolProtocol.java index 7c6203d86..615ef536d 100644 --- a/src/main/java/org/traccar/protocol/SkypatrolProtocol.java +++ b/src/main/java/org/traccar/protocol/SkypatrolProtocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class SkypatrolProtocol extends BaseProtocol { - public SkypatrolProtocol() { - addServer(new TrackerServer(true, getName()) { + @Inject + public SkypatrolProtocol(Config config) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new SkypatrolProtocolDecoder(SkypatrolProtocol.this)); } }); diff --git a/src/main/java/org/traccar/protocol/SkypatrolProtocolDecoder.java b/src/main/java/org/traccar/protocol/SkypatrolProtocolDecoder.java index 8aae310bb..6ffcbbe44 100644 --- a/src/main/java/org/traccar/protocol/SkypatrolProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/SkypatrolProtocolDecoder.java @@ -20,8 +20,7 @@ import io.netty.channel.Channel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.config.Keys; import org.traccar.helper.BitUtil; @@ -35,11 +34,15 @@ public class SkypatrolProtocolDecoder extends BaseProtocolDecoder { private static final Logger LOGGER = LoggerFactory.getLogger(SkypatrolProtocolDecoder.class); - private final long defaultMask; + private long defaultMask; public SkypatrolProtocolDecoder(Protocol protocol) { super(protocol); - defaultMask = Context.getConfig().getInteger(Keys.PROTOCOL_MASK.withPrefix(getProtocolName())); + } + + @Override + protected void init() { + defaultMask = getConfig().getInteger(Keys.PROTOCOL_MASK.withPrefix(getProtocolName())); } private static double convertCoordinate(long coordinate) { diff --git a/src/main/java/org/traccar/protocol/SmartSoleProtocol.java b/src/main/java/org/traccar/protocol/SmartSoleProtocol.java index bcf43f68b..e4838581a 100644 --- a/src/main/java/org/traccar/protocol/SmartSoleProtocol.java +++ b/src/main/java/org/traccar/protocol/SmartSoleProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class SmartSoleProtocol extends BaseProtocol { - public SmartSoleProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public SmartSoleProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '$')); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/SmartSoleProtocolDecoder.java b/src/main/java/org/traccar/protocol/SmartSoleProtocolDecoder.java index 04920c969..7fc38f061 100644 --- a/src/main/java/org/traccar/protocol/SmartSoleProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/SmartSoleProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/SmokeyProtocol.java b/src/main/java/org/traccar/protocol/SmokeyProtocol.java index 482c8347c..0aa2bcfa7 100644 --- a/src/main/java/org/traccar/protocol/SmokeyProtocol.java +++ b/src/main/java/org/traccar/protocol/SmokeyProtocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class SmokeyProtocol extends BaseProtocol { - public SmokeyProtocol() { - addServer(new TrackerServer(true, getName()) { + @Inject + public SmokeyProtocol(Config config) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new SmokeyProtocolDecoder(SmokeyProtocol.this)); } }); diff --git a/src/main/java/org/traccar/protocol/SmokeyProtocolDecoder.java b/src/main/java/org/traccar/protocol/SmokeyProtocolDecoder.java index 9da52e97a..2244ad289 100644 --- a/src/main/java/org/traccar/protocol/SmokeyProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/SmokeyProtocolDecoder.java @@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; diff --git a/src/main/java/org/traccar/protocol/SolarPoweredProtocol.java b/src/main/java/org/traccar/protocol/SolarPoweredProtocol.java index 53a948cdc..e00f27b9b 100644 --- a/src/main/java/org/traccar/protocol/SolarPoweredProtocol.java +++ b/src/main/java/org/traccar/protocol/SolarPoweredProtocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class SolarPoweredProtocol extends BaseProtocol { - public SolarPoweredProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public SolarPoweredProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new HuabaoFrameDecoder()); pipeline.addLast(new SolarPoweredProtocolDecoder(SolarPoweredProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/SolarPoweredProtocolDecoder.java b/src/main/java/org/traccar/protocol/SolarPoweredProtocolDecoder.java index 9d5dc072f..0432fbd03 100644 --- a/src/main/java/org/traccar/protocol/SolarPoweredProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/SolarPoweredProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.BitUtil; import org.traccar.helper.DateBuilder; diff --git a/src/main/java/org/traccar/protocol/SpotProtocol.java b/src/main/java/org/traccar/protocol/SpotProtocol.java index bbf0e8d8a..4fc57f177 100644 --- a/src/main/java/org/traccar/protocol/SpotProtocol.java +++ b/src/main/java/org/traccar/protocol/SpotProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class SpotProtocol extends BaseProtocol { - public SpotProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public SpotProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new HttpResponseEncoder()); pipeline.addLast(new HttpRequestDecoder()); pipeline.addLast(new HttpObjectAggregator(65535)); diff --git a/src/main/java/org/traccar/protocol/SpotProtocolDecoder.java b/src/main/java/org/traccar/protocol/SpotProtocolDecoder.java index 34417d95f..d493b748d 100644 --- a/src/main/java/org/traccar/protocol/SpotProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/SpotProtocolDecoder.java @@ -20,7 +20,7 @@ import io.netty.channel.Channel; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpResponseStatus; import org.traccar.BaseHttpProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.DateUtil; import org.traccar.model.Position; diff --git a/src/main/java/org/traccar/protocol/StarLinkProtocol.java b/src/main/java/org/traccar/protocol/StarLinkProtocol.java index 5630722ee..6dcd40fbf 100644 --- a/src/main/java/org/traccar/protocol/StarLinkProtocol.java +++ b/src/main/java/org/traccar/protocol/StarLinkProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class StarLinkProtocol extends BaseProtocol { - public StarLinkProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public StarLinkProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java b/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java index 7a6b6f4fe..193005e28 100644 --- a/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,8 +17,9 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; +import org.traccar.config.Keys; +import org.traccar.helper.model.AttributeUtil; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.DataConverter; import org.traccar.helper.Parser; @@ -55,17 +56,21 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder { public StarLinkProtocolDecoder(Protocol protocol) { super(protocol); + } - setFormat(Context.getConfig().getString( - getProtocolName() + ".format", "#EDT#,#EID#,#PDT#,#LAT#,#LONG#,#SPD#,#HEAD#,#ODO#," + @Override + protected void init() { + setFormat(getConfig().getString( + Keys.PROTOCOL_FORMAT.withPrefix(getProtocolName()), "#EDT#,#EID#,#PDT#,#LAT#,#LONG#,#SPD#,#HEAD#,#ODO#," + "#IN1#,#IN2#,#IN3#,#IN4#,#OUT1#,#OUT2#,#OUT3#,#OUT4#,#LAC#,#CID#,#VIN#,#VBAT#,#DEST#,#IGN#,#ENG#")); - setDateFormat(Context.getConfig().getString(getProtocolName() + ".dateFormat", "yyMMddHHmmss")); + setDateFormat(getConfig().getString(Keys.PROTOCOL_DATE_FORMAT.withPrefix(getProtocolName()), "yyMMddHHmmss")); } public String[] getFormat(long deviceId) { - return Context.getIdentityManager().lookupAttributeString( - deviceId, getProtocolName() + ".format", format, false, false).split(","); + String value = AttributeUtil.lookup( + getCacheManager(), Keys.PROTOCOL_FORMAT.withPrefix(getProtocolName()), deviceId); + return (value != null ? value : format).split(","); } public void setFormat(String format) { @@ -73,8 +78,9 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder { } public DateFormat getDateFormat(long deviceId) { - DateFormat dateFormat = new SimpleDateFormat(Context.getIdentityManager().lookupAttributeString( - deviceId, getProtocolName() + ".dateFormat", this.dateFormat, false, false)); + String value = AttributeUtil.lookup( + getCacheManager(), Keys.PROTOCOL_DATE_FORMAT.withPrefix(getProtocolName()), deviceId); + DateFormat dateFormat = new SimpleDateFormat(value != null ? value : this.dateFormat); dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); return dateFormat; } @@ -313,7 +319,7 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder { } if (lac != null && cid != null) { - position.setNetwork(new Network(CellTower.fromLacCid(lac, cid))); + position.setNetwork(new Network(CellTower.fromLacCid(getConfig(), lac, cid))); } if (event == 20) { diff --git a/src/main/java/org/traccar/protocol/StarcomProtocol.java b/src/main/java/org/traccar/protocol/StarcomProtocol.java index 63a6143a6..458220e59 100644 --- a/src/main/java/org/traccar/protocol/StarcomProtocol.java +++ b/src/main/java/org/traccar/protocol/StarcomProtocol.java @@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class StarcomProtocol extends BaseProtocol { - public StarcomProtocol() { - addServer(new TrackerServer(true, getName()) { + @Inject + public StarcomProtocol(Config config) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StarcomProtocolDecoder(StarcomProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/StarcomProtocolDecoder.java b/src/main/java/org/traccar/protocol/StarcomProtocolDecoder.java index 5ffddb318..56ab733c8 100644 --- a/src/main/java/org/traccar/protocol/StarcomProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/StarcomProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; diff --git a/src/main/java/org/traccar/protocol/StartekProtocol.java b/src/main/java/org/traccar/protocol/StartekProtocol.java index 32f1c5a29..550545345 100644 --- a/src/main/java/org/traccar/protocol/StartekProtocol.java +++ b/src/main/java/org/traccar/protocol/StartekProtocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2021 - 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. @@ -21,20 +21,24 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class StartekProtocol extends BaseProtocol { - public StartekProtocol() { + @Inject + public StartekProtocol(Config config) { setSupportedDataCommands( Command.TYPE_CUSTOM, Command.TYPE_OUTPUT_CONTROL, Command.TYPE_ENGINE_STOP, Command.TYPE_ENGINE_RESUME); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { - pipeline.addLast(new LineBasedFrameDecoder(1024)); + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(new LineBasedFrameDecoder(1100)); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StartekProtocolEncoder(StartekProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java b/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java index c1869def2..9c749c8d9 100644 --- a/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.BitUtil; import org.traccar.helper.Parser; @@ -72,21 +72,25 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder { .number("(x+)") // battery .expression("([^,]+)?") // adc .groupBegin() - .text(",") - .number("d,") // extended - .expression("([^,]+)?,") // fuel - .expression("([^,]+)?") // temperature + .number(",d+") // extended + .expression(",([^,]+)?") // fuel + .groupBegin() + .expression(",([^,]+)?") // temperature .groupBegin() .text(",") - .number("(d+)|") // rpm - .number("(d+)|") // engine load - .number("d+|") // maf flow - .number("d+|") // intake pressure - .number("d+|") // intake temperature - .number("(d+)|") // throttle - .number("(d+)|") // coolant temperature - .number("(d+)|") // instant fuel - .number("(d+)") // fuel level + .groupBegin() + .number("(d+)?|") // rpm + .number("(d+)?|") // engine load + .number("(d+)?|") // maf flow + .number("(d+)?|") // intake pressure + .number("(d+)?|") // intake temperature + .number("(d+)?|") // throttle + .number("(d+)?|") // coolant temperature + .number("(d+)?|") // instant fuel + .number("(d+)[%L]").optional() // fuel level + .groupEnd("?") + .number(",(d+)").optional() // hours + .groupEnd("?") .groupEnd("?") .groupEnd("?") .any() @@ -94,6 +98,8 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder { private String decodeAlarm(int value) { switch (value) { + case 1: + return Position.ALARM_SOS; case 5: case 6: return Position.ALARM_DOOR; @@ -182,6 +188,7 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder { int input = parser.nextHexInt(); int output = parser.nextHexInt(); position.set(Position.KEY_IGNITION, BitUtil.check(input, 1)); + position.set(Position.KEY_DOOR, BitUtil.check(input, 2)); position.set(Position.KEY_INPUT, input); position.set(Position.KEY_OUTPUT, output); @@ -217,15 +224,28 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder { } } - if (parser.hasNext(6)) { + if (parser.hasNextAny(9)) { position.set(Position.KEY_RPM, parser.nextInt()); position.set(Position.KEY_ENGINE_LOAD, parser.nextInt()); + position.set("airFlow", parser.nextInt()); + position.set("airPressure", parser.nextInt()); + if (parser.hasNext()) { + position.set("airTemp", parser.nextInt() - 40); + } position.set(Position.KEY_THROTTLE, parser.nextInt()); - position.set(Position.KEY_COOLANT_TEMP, parser.nextInt() - 40); - position.set(Position.KEY_FUEL_CONSUMPTION, parser.nextInt() * 0.1); + if (parser.hasNext()) { + position.set(Position.KEY_COOLANT_TEMP, parser.nextInt() - 40); + } + if (parser.hasNext()) { + position.set(Position.KEY_FUEL_CONSUMPTION, parser.nextInt() * 0.1); + } position.set(Position.KEY_FUEL_LEVEL, parser.nextInt()); } + if (parser.hasNext()) { + position.set(Position.KEY_HOURS, parser.nextInt() * 1000L); + } + return position; } diff --git a/src/main/java/org/traccar/protocol/StbProtocol.java b/src/main/java/org/traccar/protocol/StbProtocol.java index 002ed86c7..0beaed39c 100644 --- a/src/main/java/org/traccar/protocol/StbProtocol.java +++ b/src/main/java/org/traccar/protocol/StbProtocol.java @@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class StbProtocol extends BaseProtocol { - public StbProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public StbProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new JsonFrameDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/StbProtocolDecoder.java b/src/main/java/org/traccar/protocol/StbProtocolDecoder.java index cc985d605..c52ab485f 100644 --- a/src/main/java/org/traccar/protocol/StbProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/StbProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,19 +15,16 @@ */ package org.traccar.protocol; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.JsonProcessingException; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.model.Position; +import org.traccar.session.DeviceSession; -import javax.json.Json; -import javax.json.JsonObject; -import javax.json.JsonValue; +import jakarta.json.Json; +import jakarta.json.JsonObject; +import jakarta.json.JsonValue; import java.io.StringReader; import java.net.SocketAddress; import java.util.Date; @@ -42,29 +39,13 @@ public class StbProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_PROPERTY = 310; public static final int MSG_ALARM = 410; - public static class Response { - @JsonProperty("msgType") - private int type; - @JsonProperty("devId") - private String deviceId; - @JsonProperty("result") - private int result; - @JsonProperty("txnNo") - private String transaction; - } - private void sendResponse( - Channel channel, SocketAddress remoteAddress, int type, String deviceId, JsonObject root) - throws JsonProcessingException { - - Response response = new Response(); - response.type = type + 1; - response.deviceId = deviceId; - response.result = 1; - response.transaction = root.getString("txnNo"); + Channel channel, SocketAddress remoteAddress, int type, String deviceId, JsonObject root) { + String response = String.format( + "{ \"msgType\": %d, \"devId\": \"%s\", \"result\": 1, \"txnNo\": \"%s\" }", + type + 1, deviceId, root.getString("txnNo")); if (channel != null) { - channel.writeAndFlush(new NetworkMessage( - Context.getObjectMapper().writeValueAsString(response), remoteAddress)); + channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); } } diff --git a/src/main/java/org/traccar/protocol/Stl060Protocol.java b/src/main/java/org/traccar/protocol/Stl060Protocol.java index 2711e936b..ac23ab3ee 100644 --- a/src/main/java/org/traccar/protocol/Stl060Protocol.java +++ b/src/main/java/org/traccar/protocol/Stl060Protocol.java @@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class Stl060Protocol extends BaseProtocol { - public Stl060Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public Stl060Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new Stl060FrameDecoder(1024)); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/Stl060ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Stl060ProtocolDecoder.java index 7b0055aa1..dc1fa3ba3 100644 --- a/src/main/java/org/traccar/protocol/Stl060ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Stl060ProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/SuntechProtocol.java b/src/main/java/org/traccar/protocol/SuntechProtocol.java index 199885537..0cc5fc75c 100644 --- a/src/main/java/org/traccar/protocol/SuntechProtocol.java +++ b/src/main/java/org/traccar/protocol/SuntechProtocol.java @@ -19,11 +19,15 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class SuntechProtocol extends BaseProtocol { - public SuntechProtocol() { + @Inject + public SuntechProtocol(Config config) { setSupportedDataCommands( Command.TYPE_OUTPUT_CONTROL, Command.TYPE_REBOOT_DEVICE, @@ -32,9 +36,9 @@ public class SuntechProtocol extends BaseProtocol { Command.TYPE_ENGINE_RESUME, Command.TYPE_ALARM_ARM, Command.TYPE_ALARM_DISARM); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new SuntechFrameDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new SuntechProtocolEncoder(SuntechProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java index 8926f427e..86a8bf6fe 100644 --- a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java @@ -20,8 +20,10 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; +import org.traccar.config.Keys; +import org.traccar.helper.BufferUtil; +import org.traccar.helper.model.AttributeUtil; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.BitUtil; import org.traccar.helper.DateBuilder; @@ -41,6 +43,7 @@ import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.TimeZone; +import java.util.stream.Collectors; public class SuntechProtocolDecoder extends BaseProtocolDecoder { @@ -72,8 +75,8 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { } public int getProtocolType(long deviceId) { - return Context.getIdentityManager().lookupAttributeInteger( - deviceId, getProtocolName() + ".protocolType", protocolType, false, true); + Integer value = AttributeUtil.lookup(getCacheManager(), Keys.PROTOCOL_TYPE, deviceId); + return value != null ? value : protocolType; } public void setHbm(boolean hbm) { @@ -81,8 +84,8 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { } public boolean isHbm(long deviceId) { - return Context.getIdentityManager().lookupAttributeBoolean( - deviceId, getProtocolName() + ".hbm", hbm, false, true); + Boolean value = AttributeUtil.lookup(getCacheManager(), Keys.PROTOCOL_HBM, deviceId); + return value != null ? value : hbm; } public void setIncludeAdc(boolean includeAdc) { @@ -90,8 +93,9 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { } public boolean isIncludeAdc(long deviceId) { - return Context.getIdentityManager().lookupAttributeBoolean( - deviceId, getProtocolName() + ".includeAdc", includeAdc, false, true); + Boolean value = AttributeUtil.lookup( + getCacheManager(), Keys.PROTOCOL_INCLUDE_ADC.withPrefix(getProtocolName()), deviceId); + return value != null ? value : includeAdc; } public void setIncludeRpm(boolean includeRpm) { @@ -99,8 +103,9 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { } public boolean isIncludeRpm(long deviceId) { - return Context.getIdentityManager().lookupAttributeBoolean( - deviceId, getProtocolName() + ".includeRpm", includeRpm, false, true); + Boolean value = AttributeUtil.lookup( + getCacheManager(), Keys.PROTOCOL_INCLUDE_RPM.withPrefix(getProtocolName()), deviceId); + return value != null ? value : includeRpm; } public void setIncludeTemp(boolean includeTemp) { @@ -108,8 +113,9 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { } public boolean isIncludeTemp(long deviceId) { - return Context.getIdentityManager().lookupAttributeBoolean( - deviceId, getProtocolName() + ".includeTemp", includeTemp, false, true); + Boolean value = AttributeUtil.lookup( + getCacheManager(), Keys.PROTOCOL_INCLUDE_TEMPERATURE.withPrefix(getProtocolName()), deviceId); + return value != null ? value : includeTemp; } private Position decode9( @@ -198,12 +204,16 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { return Position.ALARM_POWER_RESTORED; case 41: return Position.ALARM_POWER_CUT; + case 42: + return Position.ALARM_SOS; case 46: return Position.ALARM_ACCELERATION; case 47: return Position.ALARM_BRAKING; case 50: return Position.ALARM_JAMMING; + case 132: + return Position.ALARM_DOOR; default: return null; } @@ -261,18 +271,26 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { index += 1; // collaborative network } - DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHH:mm:ss"); - dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - position.setTime(dateFormat.parse(values[index++] + values[index++])); + if (values[index].isEmpty()) { - position.setLatitude(Double.parseDouble(values[index++])); - position.setLongitude(Double.parseDouble(values[index++])); - position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(values[index++]))); - position.setCourse(Double.parseDouble(values[index++])); + getLastLocation(position, null); - position.set(Position.KEY_SATELLITES, Integer.parseInt(values[index++])); + } else { - position.setValid(values[index++].equals("1")); + DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHH:mm:ss"); + dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + position.setTime(dateFormat.parse(values[index++] + values[index++])); + + position.setLatitude(Double.parseDouble(values[index++])); + position.setLongitude(Double.parseDouble(values[index++])); + position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(values[index++]))); + position.setCourse(Double.parseDouble(values[index++])); + + position.set(Position.KEY_SATELLITES, Integer.parseInt(values[index++])); + + position.setValid(values[index++].equals("1")); + + } return position; } @@ -371,7 +389,7 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { } } else if (attribute.startsWith("GTSL")) { position.set(Position.KEY_DRIVER_UNIQUE_ID, attribute.split("\\|")[4]); - } else { + } else if (attribute.contains("=")) { String[] pair = attribute.split("="); if (pair.length >= 2) { String value = pair[1].trim(); @@ -394,6 +412,8 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { break; } } + } else { + position.set("serial", attribute.trim()); } remaining -= attribute.length() + 1; } @@ -461,7 +481,7 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { String type = values[index++]; - if (!type.equals("STT") && !type.equals("ALT") && !type.equals("BLE")) { + if (!type.equals("STT") && !type.equals("ALT") && !type.equals("BLE") && !type.equals("RES")) { return null; } @@ -474,6 +494,14 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { position.setDeviceId(deviceSession.getDeviceId()); position.set(Position.KEY_TYPE, type); + if (type.equals("RES")) { + getLastLocation(position, null); + position.set( + Position.KEY_RESULT, + Arrays.stream(values, index, values.length).collect(Collectors.joining(";"))); + return position; + } + int mask; if (type.equals("BLE")) { mask = 0b1100000110110; @@ -563,7 +591,9 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { } if (BitUtil.check(mask, 17)) { - position.set(Position.KEY_INPUT, Integer.parseInt(values[index++])); + int input = Integer.parseInt(values[index++]); + position.set(Position.KEY_IGNITION, BitUtil.check(input, 0)); + position.set(Position.KEY_INPUT, input); } if (BitUtil.check(mask, 18)) { @@ -572,7 +602,8 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { if (type.equals("ALT")) { if (BitUtil.check(mask, 19)) { - position.set("alertId", values[index++]); + int alertId = Integer.parseInt(values[index++]); + position.set(Position.KEY_ALARM, decodeAlert(alertId)); } if (BitUtil.check(mask, 20)) { position.set("alertModifier", values[index++]); @@ -667,19 +698,11 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { } if (BitUtil.check(mask, 11)) { - long value = buf.readUnsignedInt(); - if (BitUtil.check(value, 31)) { - value = -BitUtil.to(value, 31); - } - position.setLatitude(value / 1000000.0); + position.setLatitude(BufferUtil.readSignedMagnitudeInt(buf) / 1000000.0); } if (BitUtil.check(mask, 12)) { - long value = buf.readUnsignedInt(); - if (BitUtil.check(value, 31)) { - value = -BitUtil.to(value, 31); - } - position.setLongitude(value / 1000000.0); + position.setLongitude(BufferUtil.readSignedMagnitudeInt(buf) / 1000000.0); } if (BitUtil.check(mask, 13)) { @@ -841,7 +864,7 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { } else { - String[] values = buf.toString(StandardCharsets.US_ASCII).split(";"); + String[] values = buf.toString(StandardCharsets.US_ASCII).split(";", -1); prefix = values[0]; if (prefix.equals("CRR")) { diff --git a/src/main/java/org/traccar/protocol/SuntechProtocolEncoder.java b/src/main/java/org/traccar/protocol/SuntechProtocolEncoder.java index 597acaae8..a4faacf13 100644 --- a/src/main/java/org/traccar/protocol/SuntechProtocolEncoder.java +++ b/src/main/java/org/traccar/protocol/SuntechProtocolEncoder.java @@ -45,13 +45,13 @@ public class SuntechProtocolEncoder extends StringProtocolEncoder { } if (universal) { - return encodeUniversalCommand(channel, command); + return encodeUniversalCommand(command); } else { - return encodeLegacyCommand(channel, prefix, command); + return encodeLegacyCommand(prefix, command); } } - protected Object encodeUniversalCommand(Channel channel, Command command) { + protected Object encodeUniversalCommand(Command command) { switch (command.getType()) { case Command.TYPE_REBOOT_DEVICE: return formatCommand(command, "CMD;%s;03;03\r", Command.KEY_UNIQUE_ID); @@ -59,23 +59,23 @@ public class SuntechProtocolEncoder extends StringProtocolEncoder { return formatCommand(command, "CMD;%s;03;01\r", Command.KEY_UNIQUE_ID); case Command.TYPE_OUTPUT_CONTROL: if (command.getAttributes().get(Command.KEY_DATA).equals("1")) { - switch (command.getString(Command.KEY_INDEX)) { - case "1": + switch (command.getInteger(Command.KEY_INDEX)) { + case 1: return formatCommand(command, "CMD;%s;04;01\r", Command.KEY_UNIQUE_ID); - case "2": + case 2: return formatCommand(command, "CMD;%s;04;03\r", Command.KEY_UNIQUE_ID); - case "3": + case 3: return formatCommand(command, "CMD;%s;04;09\r", Command.KEY_UNIQUE_ID); default: return null; } } else { - switch (command.getString(Command.KEY_INDEX)) { - case "1": + switch (command.getInteger(Command.KEY_INDEX)) { + case 1: return formatCommand(command, "CMD;%s;04;02\r", Command.KEY_UNIQUE_ID); - case "2": + case 2: return formatCommand(command, "CMD;%s;04;04\r", Command.KEY_UNIQUE_ID); - case "3": + case 3: return formatCommand(command, "CMD;%s;04;10\r", Command.KEY_UNIQUE_ID); default: return null; @@ -84,7 +84,7 @@ public class SuntechProtocolEncoder extends StringProtocolEncoder { case Command.TYPE_ENGINE_STOP: return formatCommand(command, "CMD;%s;04;01\r", Command.KEY_UNIQUE_ID); case Command.TYPE_ENGINE_RESUME: - return formatCommand(command, "CMD;%s;02;02\r", Command.KEY_UNIQUE_ID); + return formatCommand(command, "CMD;%s;04;02\r", Command.KEY_UNIQUE_ID); case Command.TYPE_ALARM_ARM: return formatCommand(command, "CMD;%s;04;03\r", Command.KEY_UNIQUE_ID); case Command.TYPE_ALARM_DISARM: @@ -94,12 +94,12 @@ public class SuntechProtocolEncoder extends StringProtocolEncoder { } } - protected Object encodeLegacyCommand(Channel channel, String prefix, Command command) { + protected Object encodeLegacyCommand(String prefix, Command command) { switch (command.getType()) { case Command.TYPE_REBOOT_DEVICE: return formatCommand(command, prefix + "CMD;%s;02;Reboot\r", Command.KEY_UNIQUE_ID); case Command.TYPE_POSITION_SINGLE: - return formatCommand(command, prefix + "CMD;%s;02;\r", Command.KEY_UNIQUE_ID); + return formatCommand(command, prefix + "CMD;%s;02;StatusReq\r", Command.KEY_UNIQUE_ID); case Command.TYPE_OUTPUT_CONTROL: if (command.getAttributes().get(Command.KEY_DATA).equals("1")) { return formatCommand(command, prefix + "CMD;%s;02;Enable%s\r", diff --git a/src/main/java/org/traccar/protocol/SupermateProtocol.java b/src/main/java/org/traccar/protocol/SupermateProtocol.java index 46625ddc7..064f12b4b 100644 --- a/src/main/java/org/traccar/protocol/SupermateProtocol.java +++ b/src/main/java/org/traccar/protocol/SupermateProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class SupermateProtocol extends BaseProtocol { - public SupermateProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public SupermateProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "#")); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/SupermateProtocolDecoder.java b/src/main/java/org/traccar/protocol/SupermateProtocolDecoder.java index 40a25bb91..f53f0f598 100644 --- a/src/main/java/org/traccar/protocol/SupermateProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/SupermateProtocolDecoder.java @@ -18,7 +18,7 @@ package org.traccar.protocol; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; diff --git a/src/main/java/org/traccar/protocol/SviasProtocol.java b/src/main/java/org/traccar/protocol/SviasProtocol.java index accfa173f..a903d503c 100644 --- a/src/main/java/org/traccar/protocol/SviasProtocol.java +++ b/src/main/java/org/traccar/protocol/SviasProtocol.java @@ -21,12 +21,15 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; - +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class SviasProtocol extends BaseProtocol { - public SviasProtocol() { + @Inject + public SviasProtocol(Config config) { setSupportedDataCommands( Command.TYPE_CUSTOM, Command.TYPE_POSITION_SINGLE, @@ -36,9 +39,9 @@ public class SviasProtocol extends BaseProtocol { Command.TYPE_ALARM_ARM, Command.TYPE_ALARM_DISARM, Command.TYPE_ALARM_REMOVE); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "]")); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/SviasProtocolDecoder.java b/src/main/java/org/traccar/protocol/SviasProtocolDecoder.java index 7e783f6cd..d7b126167 100644 --- a/src/main/java/org/traccar/protocol/SviasProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/SviasProtocolDecoder.java @@ -24,7 +24,7 @@ import org.traccar.helper.PatternBuilder; import java.net.SocketAddress; import java.util.regex.Pattern; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.helper.Parser; import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; diff --git a/src/main/java/org/traccar/protocol/SwiftechProtocol.java b/src/main/java/org/traccar/protocol/SwiftechProtocol.java index 5e2597b93..d5fa5c5d3 100644 --- a/src/main/java/org/traccar/protocol/SwiftechProtocol.java +++ b/src/main/java/org/traccar/protocol/SwiftechProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class SwiftechProtocol extends BaseProtocol { - public SwiftechProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public SwiftechProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '#')); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/SwiftechProtocolDecoder.java b/src/main/java/org/traccar/protocol/SwiftechProtocolDecoder.java index 8d0b31c8f..b1cff8b64 100644 --- a/src/main/java/org/traccar/protocol/SwiftechProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/SwiftechProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/T55Protocol.java b/src/main/java/org/traccar/protocol/T55Protocol.java index f5ec19094..e76959fea 100644 --- a/src/main/java/org/traccar/protocol/T55Protocol.java +++ b/src/main/java/org/traccar/protocol/T55Protocol.java @@ -21,22 +21,26 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class T55Protocol extends BaseProtocol { - public T55Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public T55Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new T55ProtocolDecoder(T55Protocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new T55ProtocolDecoder(T55Protocol.this)); diff --git a/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java b/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java index 230d29216..b18359b3f 100644 --- a/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 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. @@ -17,8 +17,9 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; +import org.traccar.config.Keys; +import org.traccar.helper.model.AttributeUtil; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; @@ -27,6 +28,7 @@ import org.traccar.helper.PatternBuilder; import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; +import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.channels.DatagramChannel; import java.util.Date; @@ -124,15 +126,53 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder { .expression("([01])") // ignition .compile(); + private static final Pattern PATTERN_PUBX = new PatternBuilder() + .text("$PUBX,") + .number("(d+),") // index + .number("(dd)(dd)(dd).d+,") // time (hhmmss) + .number("(dd)(dd.d+),([NS]),") // latitude + .number("(ddd)(dd.d+),([EW]),") // longitude + .number("(-?d+.d+),") // altitude + .expression("(..),") // status + .number("(d+.d+),") // horizontal accuracy + .number("d+.d+,") // vertical accuracy + .number("(d+.d+),") // speed + .number("(d+.d+),") // course + .number("-?d+.d+,") // vertical velocity + .expression("[^,]*,") // corrections age + .number("(d+.d+),") // hdop + .number("(d+.d+),") // vdop + .number("d+.d+,") // tdop + .number("(d+),") // satellites + .number("(d+),") // device id + .number("d+") + .text("*") + .number("xx") // checksum + .compile(); + + private static final Pattern PATTERN_GPTXT = new PatternBuilder() + .text("$GPTXT,") + .text("NET,") + .number("(d+),") // device id + .expression("([^,]+),") // network operator + .number("(-d+),") // rssi + .number("(d+) ") // mcc + .number("(d+)") // mnc + .text("*") + .number("xx") // checksum + .compile(); + private Position position = null; private Position decodeGprmc( DeviceSession deviceSession, String sentence, SocketAddress remoteAddress, Channel channel) { - if (deviceSession != null && channel != null && !(channel instanceof DatagramChannel) - && Context.getIdentityManager().lookupAttributeBoolean( - deviceSession.getDeviceId(), getProtocolName() + ".ack", false, false, true)) { - channel.writeAndFlush(new NetworkMessage("OK1\r\n", remoteAddress)); + if (deviceSession != null && channel != null && !(channel instanceof DatagramChannel)) { + boolean ack = AttributeUtil.lookup( + getCacheManager(), Keys.PROTOCOL_ACK.withPrefix(getProtocolName()), deviceSession.getDeviceId()); + if (ack) { + channel.writeAndFlush(new NetworkMessage("OK1\r\n", remoteAddress)); + } } Parser parser = new Parser(PATTERN_GPRMC, sentence); @@ -300,11 +340,69 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder { return position; } + private Position decodeGptxt(Channel channel, SocketAddress remoteAddress, String sentence) { + + Parser parser = new Parser(PATTERN_GPTXT, sentence); + if (!parser.matches()) { + return null; + } + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + getLastLocation(position, null); + + position.set(Position.KEY_OPERATOR, parser.next()); + position.set(Position.KEY_RSSI, parser.nextInt()); + position.set("mcc", parser.nextInt()); + position.set("mnc", parser.nextInt()); + + return position; + } + + private Position decodePubx(Channel channel, SocketAddress remoteAddress, String sentence) { + + Parser parser = new Parser(PATTERN_PUBX, sentence); + if (!parser.matches()) { + return null; + } + + Position position = new Position(getProtocolName()); + + position.set(Position.KEY_INDEX, parser.nextInt()); + + position.setTime(parser.nextDateTime(Parser.DateTimeFormat.HMS)); + position.setLatitude(parser.nextCoordinate()); + position.setLongitude(parser.nextCoordinate()); + position.setAltitude(parser.nextDouble()); + position.setValid(!parser.next().equals("NF")); + position.setAccuracy(parser.nextDouble()); + position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble())); + position.setCourse(parser.nextDouble()); + + position.set(Position.KEY_HDOP, parser.nextDouble()); + position.set(Position.KEY_VDOP, parser.nextDouble()); + position.set(Position.KEY_SATELLITES, parser.nextInt()); + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession != null) { + position.setDeviceId(deviceSession.getDeviceId()); + return position; + } + + return null; + } + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { - String sentence = (String) msg; + String sentence = ((String) msg).trim(); DeviceSession deviceSession; @@ -320,6 +418,10 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder { sentence = sentence.substring(index); } else { deviceSession = getDeviceSession(channel, remoteAddress); + if (deviceSession == null && remoteAddress instanceof InetSocketAddress) { + String host = ((InetSocketAddress) remoteAddress).getHostString(); + deviceSession = getDeviceSession(channel, remoteAddress, host); + } } if (sentence.startsWith("$PGID")) { @@ -354,6 +456,10 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder { return decodeGpiop(deviceSession, sentence); } else if (sentence.startsWith("QZE")) { return decodeQze(channel, remoteAddress, sentence); + } else if (sentence.startsWith("$PUBX")) { + return decodePubx(channel, remoteAddress, sentence); + } else if (sentence.startsWith("$GPTXT")) { + return decodeGptxt(channel, remoteAddress, sentence); } return null; diff --git a/src/main/java/org/traccar/protocol/T57Protocol.java b/src/main/java/org/traccar/protocol/T57Protocol.java index f67f82318..e6ef4ccc9 100644 --- a/src/main/java/org/traccar/protocol/T57Protocol.java +++ b/src/main/java/org/traccar/protocol/T57Protocol.java @@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class T57Protocol extends BaseProtocol { - public T57Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public T57Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new T57FrameDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/T57ProtocolDecoder.java b/src/main/java/org/traccar/protocol/T57ProtocolDecoder.java index 2a3cca3e4..d9fd1c8cf 100644 --- a/src/main/java/org/traccar/protocol/T57ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/T57ProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/T622IridiumProtocol.java b/src/main/java/org/traccar/protocol/T622IridiumProtocol.java new file mode 100644 index 000000000..22efa38a8 --- /dev/null +++ b/src/main/java/org/traccar/protocol/T622IridiumProtocol.java @@ -0,0 +1,39 @@ +/* + * 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.protocol; + +import io.netty.handler.codec.LengthFieldBasedFrameDecoder; +import org.traccar.BaseProtocol; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; + +public class T622IridiumProtocol extends BaseProtocol { + + @Inject + public T622IridiumProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 1, 2)); + pipeline.addLast(new T622IridiumProtocolDecoder(T622IridiumProtocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/T622IridiumProtocolDecoder.java b/src/main/java/org/traccar/protocol/T622IridiumProtocolDecoder.java new file mode 100644 index 000000000..9e64ec9be --- /dev/null +++ b/src/main/java/org/traccar/protocol/T622IridiumProtocolDecoder.java @@ -0,0 +1,149 @@ +/* + * 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.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.Protocol; +import org.traccar.config.Keys; +import org.traccar.helper.UnitsConverter; +import org.traccar.helper.model.AttributeUtil; +import org.traccar.model.Position; +import org.traccar.session.DeviceSession; + +import java.net.SocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +public class T622IridiumProtocolDecoder extends BaseProtocolDecoder { + + private String format; + + public T622IridiumProtocolDecoder(Protocol protocol) { + super(protocol); + } + + public List<Integer> getParameters(long deviceId) { + String value = AttributeUtil.lookup( + getCacheManager(), Keys.PROTOCOL_FORMAT.withPrefix(getProtocolName()), deviceId); + return Arrays.stream((value != null ? value : format).split(",")) + .map(s -> Integer.parseInt(s, 16)) + .collect(Collectors.toList()); + } + + public void setFormat(String format) { + this.format = format; + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + ByteBuf buf = (ByteBuf) msg; + + buf.readUnsignedByte(); // protocol revision + buf.readUnsignedShort(); // length + buf.readUnsignedByte(); // header indicator + buf.readUnsignedShort(); // header length + buf.readUnsignedInt(); // reference + + String imei = buf.readCharSequence(15, StandardCharsets.US_ASCII).toString(); + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei); + if (deviceSession == null) { + return null; + } + + buf.readUnsignedByte(); // session status + buf.readUnsignedShort(); // originator index + buf.readUnsignedShort(); // transfer index + buf.readUnsignedInt(); // session time + buf.readUnsignedByte(); // payload indicator + buf.readUnsignedShort(); // payload length + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + List<Integer> parameters = getParameters(deviceSession.getDeviceId()); + + for (int parameter : parameters) { + switch (parameter) { + case 0x01: + position.set(Position.KEY_EVENT, buf.readUnsignedByte()); + break; + case 0x02: + position.setLatitude(buf.readIntLE() / 1000000.0); + break; + case 0x03: + position.setLongitude(buf.readIntLE() / 1000000.0); + break; + case 0x04: + position.setTime(new Date((buf.readUnsignedIntLE() + 946684800) * 1000)); + break; + case 0x05: + position.setValid(buf.readUnsignedByte() > 0); + break; + case 0x06: + position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); + break; + case 0x07: + position.set(Position.KEY_RSSI, buf.readUnsignedByte()); + break; + case 0x08: + position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShortLE())); + break; + case 0x09: + position.setCourse(buf.readUnsignedShortLE()); + break; + case 0x0A: + position.set(Position.KEY_HDOP, buf.readUnsignedByte() * 0.1); + break; + case 0x0B: + position.setAltitude(buf.readShortLE()); + break; + case 0x0C: + position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE()); + break; + case 0x0D: + position.set(Position.KEY_HOURS, buf.readUnsignedIntLE() * 1000); + break; + case 0x14: + position.set(Position.KEY_OUTPUT, buf.readUnsignedByte()); + break; + case 0x15: + position.set(Position.KEY_INPUT, buf.readUnsignedByte()); + break; + case 0x19: + position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() * 0.01); + break; + case 0x1A: + position.set(Position.KEY_POWER, buf.readUnsignedShortLE() * 0.01); + break; + case 0x1B: + buf.readUnsignedByte(); // geofence + break; + default: + break; + } + } + + return position; + } + +} diff --git a/src/main/java/org/traccar/protocol/T800xProtocol.java b/src/main/java/org/traccar/protocol/T800xProtocol.java index 8b91265cb..f50f22a18 100644 --- a/src/main/java/org/traccar/protocol/T800xProtocol.java +++ b/src/main/java/org/traccar/protocol/T800xProtocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org) + * 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. @@ -19,21 +19,32 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class T800xProtocol extends BaseProtocol { - public T800xProtocol() { + @Inject + public T800xProtocol(Config config) { setSupportedDataCommands( Command.TYPE_CUSTOM); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 3, 2, -5, 0)); pipeline.addLast(new T800xProtocolEncoder(T800xProtocol.this)); pipeline.addLast(new T800xProtocolDecoder(T800xProtocol.this)); } }); + addServer(new TrackerServer(config, getName(), true) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(new T800xProtocolEncoder(T800xProtocol.this)); + pipeline.addLast(new T800xProtocolDecoder(T800xProtocol.this)); + } + }); } } diff --git a/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java b/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java index d554c2999..4ddea730c 100644 --- a/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 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. @@ -20,7 +20,9 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.config.Keys; +import org.traccar.helper.model.AttributeUtil; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BcdUtil; @@ -58,6 +60,7 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_DRIVER_BEHAVIOR_1 = 0x05; // 0x2626 public static final int MSG_DRIVER_BEHAVIOR_2 = 0x06; // 0x2626 public static final int MSG_BLE = 0x10; + public static final int MSG_NETWORK_2 = 0x11; public static final int MSG_GPS_2 = 0x13; public static final int MSG_ALARM_2 = 0x14; public static final int MSG_COMMAND = 0x81; @@ -167,7 +170,7 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder { return decodePosition(channel, deviceSession, buf, type, index, imei); - } else if (type == MSG_NETWORK && header == 0x2727) { + } else if (type == MSG_NETWORK && header == 0x2727 || type == MSG_NETWORK_2) { Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); @@ -397,6 +400,7 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder { int alarm = buf.readUnsignedByte(); position.set(Position.KEY_ALARM, header != 0x2727 ? decodeAlarm1(alarm) : decodeAlarm2(alarm)); + position.set("alarmCode", alarm); if (header != 0x2727) { @@ -467,6 +471,7 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder { int inputStatus = buf.readUnsignedShort(); position.set(Position.KEY_IGNITION, BitUtil.check(inputStatus, 2)); position.set(Position.KEY_RSSI, BitUtil.between(inputStatus, 4, 11)); + position.set(Position.KEY_INPUT, inputStatus); buf.readUnsignedShort(); // ignition on upload interval buf.readUnsignedInt(); // ignition off upload interval @@ -510,7 +515,9 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder { } } - if (type == MSG_ALARM || type == MSG_ALARM_2) { + boolean acknowledgement = AttributeUtil.lookup( + getCacheManager(), Keys.PROTOCOL_ACK.withPrefix(getProtocolName()), deviceSession.getDeviceId()); + if (acknowledgement || type == MSG_ALARM || type == MSG_ALARM_2) { sendResponse(channel, header, type, index, imei, alarm); } diff --git a/src/main/java/org/traccar/protocol/TaipPrefixEncoder.java b/src/main/java/org/traccar/protocol/TaipPrefixEncoder.java index 02c111b01..48419af2a 100644 --- a/src/main/java/org/traccar/protocol/TaipPrefixEncoder.java +++ b/src/main/java/org/traccar/protocol/TaipPrefixEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,13 +15,14 @@ */ package org.traccar.protocol; +import com.google.inject.Inject; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageEncoder; -import org.traccar.Context; import org.traccar.Protocol; +import org.traccar.config.Config; import org.traccar.config.Keys; import java.util.List; @@ -30,14 +31,20 @@ import java.util.List; public class TaipPrefixEncoder extends MessageToMessageEncoder<ByteBuf> { private final Protocol protocol; + private Config config; public TaipPrefixEncoder(Protocol protocol) { this.protocol = protocol; } + @Inject + public void setConfig(Config config) { + this.config = config; + } + @Override - protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception { - if (Context.getConfig().getBoolean(Keys.PROTOCOL_PREFIX.withPrefix(protocol.getName()))) { + protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) { + if (config.getBoolean(Keys.PROTOCOL_PREFIX.withPrefix(protocol.getName()))) { out.add(Unpooled.wrappedBuffer(Unpooled.wrappedBuffer(new byte[] {0x20, 0x20, 0x06, 0x00}), msg.retain())); } else { out.add(msg.retain()); diff --git a/src/main/java/org/traccar/protocol/TaipProtocol.java b/src/main/java/org/traccar/protocol/TaipProtocol.java index 0966cfd7c..71ab485ca 100644 --- a/src/main/java/org/traccar/protocol/TaipProtocol.java +++ b/src/main/java/org/traccar/protocol/TaipProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class TaipProtocol extends BaseProtocol { - public TaipProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public TaipProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '<')); pipeline.addLast(new TaipPrefixEncoder(TaipProtocol.this)); pipeline.addLast(new StringDecoder()); @@ -35,9 +39,9 @@ public class TaipProtocol extends BaseProtocol { pipeline.addLast(new TaipProtocolDecoder(TaipProtocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new TaipPrefixEncoder(TaipProtocol.this)); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/TaipProtocolDecoder.java b/src/main/java/org/traccar/protocol/TaipProtocolDecoder.java index ec0ce1931..787ed1599 100644 --- a/src/main/java/org/traccar/protocol/TaipProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TaipProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; @@ -26,7 +26,6 @@ import org.traccar.helper.DateBuilder; import org.traccar.helper.DateUtil; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; -import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; import java.net.SocketAddress; @@ -192,7 +191,7 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder { position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN)); } - position.setSpeed(UnitsConverter.knotsFromMph(parser.nextDouble(0))); + position.setSpeed(convertSpeed(parser.nextDouble(0), "mph")); position.setCourse(parser.nextDouble(0)); if (parser.hasNext(2)) { diff --git a/src/main/java/org/traccar/protocol/TechTltProtocol.java b/src/main/java/org/traccar/protocol/TechTltProtocol.java index 0cffb452d..a4a7460b0 100644 --- a/src/main/java/org/traccar/protocol/TechTltProtocol.java +++ b/src/main/java/org/traccar/protocol/TechTltProtocol.java @@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class TechTltProtocol extends BaseProtocol { - public TechTltProtocol() { - addServer(new TrackerServer(true, getName()) { + @Inject + public TechTltProtocol(Config config) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new TechTltProtocolDecoder(TechTltProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/TechTltProtocolDecoder.java b/src/main/java/org/traccar/protocol/TechTltProtocolDecoder.java index 17f5c80fa..94efacc63 100644 --- a/src/main/java/org/traccar/protocol/TechTltProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TechTltProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; @@ -110,7 +110,7 @@ public class TechTltProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_SATELLITES, parser.nextInt()); - position.setNetwork(new Network(CellTower.fromLacCid(parser.nextInt(), parser.nextInt()))); + position.setNetwork(new Network(CellTower.fromLacCid(getConfig(), parser.nextInt(), parser.nextInt()))); return position; } diff --git a/src/main/java/org/traccar/protocol/TechtoCruzProtocol.java b/src/main/java/org/traccar/protocol/TechtoCruzProtocol.java index a217ea738..f0828a99e 100644 --- a/src/main/java/org/traccar/protocol/TechtoCruzProtocol.java +++ b/src/main/java/org/traccar/protocol/TechtoCruzProtocol.java @@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class TechtoCruzProtocol extends BaseProtocol { - public TechtoCruzProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public TechtoCruzProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new TechtoCruzFrameDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/TechtoCruzProtocolDecoder.java b/src/main/java/org/traccar/protocol/TechtoCruzProtocolDecoder.java index 6b9f0edb6..09efcb7d4 100644 --- a/src/main/java/org/traccar/protocol/TechtoCruzProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TechtoCruzProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/TekProtocol.java b/src/main/java/org/traccar/protocol/TekProtocol.java index c1d78e6f5..56714041b 100644 --- a/src/main/java/org/traccar/protocol/TekProtocol.java +++ b/src/main/java/org/traccar/protocol/TekProtocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class TekProtocol extends BaseProtocol { - public TekProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public TekProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new TekFrameDecoder()); pipeline.addLast(new TekProtocolDecoder(TekProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/TekProtocolDecoder.java b/src/main/java/org/traccar/protocol/TekProtocolDecoder.java index 33ff51d2d..819c7e819 100644 --- a/src/main/java/org/traccar/protocol/TekProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TekProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.BitUtil; import org.traccar.helper.DateBuilder; diff --git a/src/main/java/org/traccar/protocol/TelemaxProtocol.java b/src/main/java/org/traccar/protocol/TelemaxProtocol.java index 838da9df1..792a5b176 100644 --- a/src/main/java/org/traccar/protocol/TelemaxProtocol.java +++ b/src/main/java/org/traccar/protocol/TelemaxProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class TelemaxProtocol extends BaseProtocol { - public TelemaxProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public TelemaxProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/TelemaxProtocolDecoder.java b/src/main/java/org/traccar/protocol/TelemaxProtocolDecoder.java index 9369ab101..f6f6f5379 100644 --- a/src/main/java/org/traccar/protocol/TelemaxProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TelemaxProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.BitUtil; import org.traccar.model.Position; diff --git a/src/main/java/org/traccar/protocol/TelicProtocol.java b/src/main/java/org/traccar/protocol/TelicProtocol.java index 991befa19..fc5bdf0d1 100644 --- a/src/main/java/org/traccar/protocol/TelicProtocol.java +++ b/src/main/java/org/traccar/protocol/TelicProtocol.java @@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class TelicProtocol extends BaseProtocol { - public TelicProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public TelicProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new TelicFrameDecoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/TelicProtocolDecoder.java b/src/main/java/org/traccar/protocol/TelicProtocolDecoder.java index a4f9e2989..9681dc565 100644 --- a/src/main/java/org/traccar/protocol/TelicProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TelicProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/TeltonikaFrameDecoder.java b/src/main/java/org/traccar/protocol/TeltonikaFrameDecoder.java index c30fee6e3..3a0962584 100644 --- a/src/main/java/org/traccar/protocol/TeltonikaFrameDecoder.java +++ b/src/main/java/org/traccar/protocol/TeltonikaFrameDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 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. @@ -30,7 +30,7 @@ public class TeltonikaFrameDecoder extends BaseFrameDecoder { ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception { while (buf.isReadable() && buf.getByte(buf.readerIndex()) == (byte) 0xff) { - buf.skipBytes(1); + return buf.readRetainedSlice(1); } if (buf.readableBytes() < MESSAGE_MINIMUM_LENGTH) { diff --git a/src/main/java/org/traccar/protocol/TeltonikaProtocol.java b/src/main/java/org/traccar/protocol/TeltonikaProtocol.java index 5817b86be..f2d610251 100644 --- a/src/main/java/org/traccar/protocol/TeltonikaProtocol.java +++ b/src/main/java/org/traccar/protocol/TeltonikaProtocol.java @@ -18,24 +18,28 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class TeltonikaProtocol extends BaseProtocol { - public TeltonikaProtocol() { + @Inject + public TeltonikaProtocol(Config config) { setSupportedDataCommands( Command.TYPE_CUSTOM); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new TeltonikaFrameDecoder()); pipeline.addLast(new TeltonikaProtocolEncoder(TeltonikaProtocol.this)); pipeline.addLast(new TeltonikaProtocolDecoder(TeltonikaProtocol.this, false)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new TeltonikaProtocolEncoder(TeltonikaProtocol.this)); pipeline.addLast(new TeltonikaProtocolDecoder(TeltonikaProtocol.this, true)); } diff --git a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java index f83a49941..e888642b4 100644 --- a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 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. @@ -20,8 +20,9 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; +import org.traccar.helper.BufferUtil; +import org.traccar.model.Device; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.config.Keys; @@ -39,11 +40,15 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.function.BiConsumer; public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { private static final int IMAGE_PACKET_MAX = 2048; + private static final Map<Integer, Map<Set<String>, BiConsumer<Position, ByteBuf>>> PARAMETERS = new HashMap<>(); + private final boolean connectionless; private boolean extended; private final Map<Long, ByteBuf> photos = new HashMap<>(); @@ -55,7 +60,11 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { public TeltonikaProtocolDecoder(Protocol protocol, boolean connectionless) { super(protocol); this.connectionless = connectionless; - this.extended = Context.getConfig().getBoolean(Keys.PROTOCOL_EXTENDED.withPrefix(getProtocolName())); + } + + @Override + protected void init() { + this.extended = getConfig().getBoolean(Keys.PROTOCOL_EXTENDED.withPrefix(getProtocolName())); } private void parseIdentification(Channel channel, SocketAddress remoteAddress, ByteBuf buf) { @@ -104,19 +113,8 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { } } - private boolean isPrintable(ByteBuf buf, int length) { - boolean printable = true; - for (int i = 0; i < length; i++) { - byte b = buf.getByte(buf.readerIndex() + i); - if (b < 32 && b != '\r' && b != '\n') { - printable = false; - break; - } - } - return printable; - } - - private void decodeSerial(Channel channel, SocketAddress remoteAddress, Position position, ByteBuf buf) { + private void decodeSerial( + Channel channel, SocketAddress remoteAddress, DeviceSession deviceSession, Position position, ByteBuf buf) { getLastLocation(position, null); @@ -145,10 +143,9 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { channel, remoteAddress, photoId, photo.writerIndex(), Math.min(IMAGE_PACKET_MAX, photo.writableBytes())); } else { - String uniqueId = Context.getIdentityManager().getById(position.getDeviceId()).getUniqueId(); photos.remove(photoId); try { - position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(uniqueId, photo, "jpg")); + position.set(Position.KEY_IMAGE, writeMediaFile(deviceSession.getUniqueId(), photo, "jpg")); } finally { photo.release(); } @@ -161,7 +158,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_TYPE, type); int length = buf.readInt(); - if (isPrintable(buf, length)) { + if (BufferUtil.isPrintable(buf, length)) { String data = buf.readSlice(length).toString(StandardCharsets.US_ASCII).trim(); if (data.startsWith("UUUUww") && data.endsWith("SSS")) { String[] values = data.substring(6, data.length() - 4).split(";"); @@ -181,191 +178,229 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { } } - private long readValue(ByteBuf buf, int length, boolean signed) { + private long readValue(ByteBuf buf, int length) { switch (length) { case 1: - return signed ? buf.readByte() : buf.readUnsignedByte(); + return buf.readUnsignedByte(); case 2: - return signed ? buf.readShort() : buf.readUnsignedShort(); + return buf.readUnsignedShort(); case 4: - return signed ? buf.readInt() : buf.readUnsignedInt(); + return buf.readUnsignedInt(); default: return buf.readLong(); } } - private void decodeOtherParameter(Position position, int id, ByteBuf buf, int length) { - switch (id) { - case 1: - case 2: - case 3: - case 4: - position.set("di" + id, readValue(buf, length, false)); - break; - case 9: - position.set(Position.PREFIX_ADC + 1, readValue(buf, length, false)); - break; - case 10: - position.set(Position.PREFIX_ADC + 2, readValue(buf, length, false)); - break; - case 16: - position.set(Position.KEY_ODOMETER, readValue(buf, length, false)); - break; - case 17: - position.set("axisX", readValue(buf, length, true)); - break; - case 18: - position.set("axisY", readValue(buf, length, true)); - break; - case 19: - position.set("axisZ", readValue(buf, length, true)); - break; - case 21: - position.set(Position.KEY_RSSI, readValue(buf, length, false)); - break; - case 25: - case 26: - case 27: - case 28: - position.set(Position.PREFIX_TEMP + (id - 24 + 4), readValue(buf, length, true) * 0.1); - break; - case 66: - position.set(Position.KEY_POWER, readValue(buf, length, false) * 0.001); - break; - case 67: - position.set(Position.KEY_BATTERY, readValue(buf, length, false) * 0.001); - break; - case 72: - case 73: - case 74: - position.set(Position.PREFIX_TEMP + (id - 71), readValue(buf, length, true) * 0.1); - break; - case 78: - long driverUniqueId = readValue(buf, length, false); - if (driverUniqueId != 0) { - position.set(Position.KEY_DRIVER_UNIQUE_ID, String.format("%016X", driverUniqueId)); - } - break; - case 80: - position.set("workMode", readValue(buf, length, false)); - break; - case 90: - position.set(Position.KEY_DOOR, readValue(buf, length, false)); - break; - case 115: - position.set(Position.KEY_COOLANT_TEMP, readValue(buf, length, true) * 0.1); - break; - case 179: - position.set(Position.PREFIX_OUT + 1, readValue(buf, length, false) == 1); - break; - case 180: - position.set(Position.PREFIX_OUT + 2, readValue(buf, length, false) == 1); - break; - case 181: - position.set(Position.KEY_PDOP, readValue(buf, length, false) * 0.1); - break; - case 182: - position.set(Position.KEY_HDOP, readValue(buf, length, false) * 0.1); - break; - case 199: - position.set(Position.KEY_ODOMETER_TRIP, readValue(buf, length, false)); - break; - case 236: - if (readValue(buf, length, false) == 1) { - position.set(Position.KEY_ALARM, Position.ALARM_GENERAL); - } - break; - case 239: - position.set(Position.KEY_IGNITION, readValue(buf, length, false) == 1); - break; - case 240: - position.set(Position.KEY_MOTION, readValue(buf, length, false) == 1); - break; - case 241: - position.set(Position.KEY_OPERATOR, readValue(buf, length, false)); - break; - case 253: - switch ((int) readValue(buf, length, false)) { - case 1: - position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION); - break; - case 2: - position.set(Position.KEY_ALARM, Position.ALARM_BRAKING); - break; - case 3: - position.set(Position.KEY_ALARM, Position.ALARM_CORNERING); - break; - default: - break; - } - break; - default: - position.set(Position.PREFIX_IO + id, readValue(buf, length, false)); - break; - } + private static void register(int id, Set<String> models, BiConsumer<Position, ByteBuf> handler) { + PARAMETERS.computeIfAbsent(id, key -> new HashMap<>()).put(models, handler); + } + + static { + var fmbXXX = Set.of( + "FMB001", "FMB010", "FMB002", "FMB020", "FMB003", "FMB110", "FMB120", "FMB122", "FMB125", "FMB130", + "FMB140", "FMU125", "FMB900", "FMB920", "FMB962", "FMB964", "FM3001", "FMB202", "FMB204", "FMB206", + "FMT100", "MTB100", "FMP100", "MSP500"); + + register(1, null, (p, b) -> p.set(Position.PREFIX_IN + 1, b.readUnsignedByte() > 0)); + register(2, null, (p, b) -> p.set(Position.PREFIX_IN + 2, b.readUnsignedByte() > 0)); + register(3, null, (p, b) -> p.set(Position.PREFIX_IN + 3, b.readUnsignedByte() > 0)); + register(4, null, (p, b) -> p.set(Position.PREFIX_IN + 4, b.readUnsignedByte() > 0)); + register(9, fmbXXX, (p, b) -> p.set(Position.PREFIX_ADC + 1, b.readUnsignedShort() * 0.001)); + register(10, fmbXXX, (p, b) -> p.set(Position.PREFIX_ADC + 2, b.readUnsignedShort() * 0.001)); + register(11, fmbXXX, (p, b) -> p.set(Position.KEY_ICCID, String.valueOf(b.readLong()))); + register(12, fmbXXX, (p, b) -> p.set(Position.KEY_FUEL_USED, b.readUnsignedInt() * 0.001)); + register(13, fmbXXX, (p, b) -> p.set(Position.KEY_FUEL_CONSUMPTION, b.readUnsignedShort() * 0.01)); + register(16, null, (p, b) -> p.set(Position.KEY_ODOMETER, b.readUnsignedInt())); + register(17, null, (p, b) -> p.set("axisX", b.readShort())); + register(18, null, (p, b) -> p.set("axisY", b.readShort())); + register(19, null, (p, b) -> p.set("axisZ", b.readShort())); + register(21, null, (p, b) -> p.set(Position.KEY_RSSI, b.readUnsignedByte())); + register(24, fmbXXX, (p, b) -> p.setSpeed(UnitsConverter.knotsFromKph(b.readUnsignedShort()))); + register(25, null, (p, b) -> p.set("bleTemp1", b.readShort() * 0.01)); + register(26, null, (p, b) -> p.set("bleTemp2", b.readShort() * 0.01)); + register(27, null, (p, b) -> p.set("bleTemp3", b.readShort() * 0.01)); + register(28, null, (p, b) -> p.set("bleTemp4", b.readShort() * 0.01)); + register(30, fmbXXX, (p, b) -> p.set("faultCount", b.readUnsignedByte())); + register(32, fmbXXX, (p, b) -> p.set(Position.KEY_COOLANT_TEMP, b.readByte())); + register(66, null, (p, b) -> p.set(Position.KEY_POWER, b.readUnsignedShort() * 0.001)); + register(67, null, (p, b) -> p.set(Position.KEY_BATTERY, b.readUnsignedShort() * 0.001)); + register(68, fmbXXX, (p, b) -> p.set("batteryCurrent", b.readUnsignedShort() * 0.001)); + register(72, fmbXXX, (p, b) -> p.set(Position.PREFIX_TEMP + 1, b.readInt() * 0.1)); + register(73, fmbXXX, (p, b) -> p.set(Position.PREFIX_TEMP + 2, b.readInt() * 0.1)); + register(74, fmbXXX, (p, b) -> p.set(Position.PREFIX_TEMP + 3, b.readInt() * 0.1)); + register(75, fmbXXX, (p, b) -> p.set(Position.PREFIX_TEMP + 4, b.readInt() * 0.1)); + register(78, null, (p, b) -> { + long driverUniqueId = b.readLong(); + if (driverUniqueId > 0) { + p.set(Position.KEY_DRIVER_UNIQUE_ID, String.format("%016X", driverUniqueId)); + } + }); + register(80, fmbXXX, (p, b) -> p.set("dataMode", b.readUnsignedByte())); + register(81, fmbXXX, (p, b) -> p.set(Position.KEY_OBD_SPEED, b.readUnsignedByte())); + register(82, fmbXXX, (p, b) -> p.set(Position.KEY_THROTTLE, b.readUnsignedByte())); + register(83, fmbXXX, (p, b) -> p.set(Position.KEY_FUEL_USED, b.readUnsignedInt() * 0.1)); + register(84, fmbXXX, (p, b) -> p.set(Position.KEY_FUEL_LEVEL, b.readUnsignedShort() * 0.1)); + register(85, fmbXXX, (p, b) -> p.set(Position.KEY_RPM, b.readUnsignedShort())); + register(87, fmbXXX, (p, b) -> p.set(Position.KEY_OBD_ODOMETER, b.readUnsignedInt())); + register(89, fmbXXX, (p, b) -> p.set("fuelLevelPercentage", b.readUnsignedByte())); + register(90, null, (p, b) -> p.set(Position.KEY_DOOR, b.readUnsignedShort())); + register(110, fmbXXX, (p, b) -> p.set(Position.KEY_FUEL_CONSUMPTION, b.readUnsignedShort() * 0.1)); + register(113, fmbXXX, (p, b) -> p.set(Position.KEY_BATTERY_LEVEL, b.readUnsignedByte())); + register(179, null, (p, b) -> p.set(Position.PREFIX_OUT + 1, b.readUnsignedByte() > 0)); + register(180, null, (p, b) -> p.set(Position.PREFIX_OUT + 2, b.readUnsignedByte() > 0)); + register(181, null, (p, b) -> p.set(Position.KEY_PDOP, b.readUnsignedShort() * 0.1)); + register(182, null, (p, b) -> p.set(Position.KEY_HDOP, b.readUnsignedShort() * 0.1)); + register(199, null, (p, b) -> p.set(Position.KEY_ODOMETER_TRIP, b.readUnsignedInt())); + register(200, fmbXXX, (p, b) -> p.set("sleepMode", b.readUnsignedByte())); + register(205, fmbXXX, (p, b) -> p.set("cid2g", b.readUnsignedShort())); + register(206, fmbXXX, (p, b) -> p.set("lac", b.readUnsignedShort())); + register(232, fmbXXX, (p, b) -> p.set("cngStatus", b.readUnsignedByte() > 0)); + register(233, fmbXXX, (p, b) -> p.set("cngUsed", b.readUnsignedInt() * 0.1)); + register(234, fmbXXX, (p, b) -> p.set("cngLevel", b.readUnsignedShort())); + register(235, fmbXXX, (p, b) -> p.set("oilLevel", b.readUnsignedByte())); + register(236, null, (p, b) -> { + p.set(Position.KEY_ALARM, b.readUnsignedByte() > 0 ? Position.ALARM_GENERAL : null); + }); + register(239, null, (p, b) -> p.set(Position.KEY_IGNITION, b.readUnsignedByte() > 0)); + register(240, null, (p, b) -> p.set(Position.KEY_MOTION, b.readUnsignedByte() > 0)); + register(241, null, (p, b) -> p.set(Position.KEY_OPERATOR, b.readUnsignedInt())); + register(246, fmbXXX, (p, b) -> { + p.set(Position.KEY_ALARM, b.readUnsignedByte() > 0 ? Position.ALARM_TOW : null); + }); + register(247, fmbXXX, (p, b) -> { + p.set(Position.KEY_ALARM, b.readUnsignedByte() > 0 ? Position.ALARM_ACCIDENT : null); + }); + register(249, fmbXXX, (p, b) -> { + p.set(Position.KEY_ALARM, b.readUnsignedByte() > 0 ? Position.ALARM_JAMMING : null); + }); + register(252, fmbXXX, (p, b) -> { + p.set(Position.KEY_ALARM, b.readUnsignedByte() > 0 ? Position.ALARM_POWER_CUT : null); + }); + register(253, null, (p, b) -> { + switch (b.readUnsignedByte()) { + case 1: + p.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION); + break; + case 2: + p.set(Position.KEY_ALARM, Position.ALARM_BRAKING); + break; + case 3: + p.set(Position.KEY_ALARM, Position.ALARM_CORNERING); + break; + default: + break; + } + }); + register(636, fmbXXX, (p, b) -> p.set("cid4g", b.readUnsignedInt())); } private void decodeGh3000Parameter(Position position, int id, ByteBuf buf, int length) { switch (id) { case 1: - position.set(Position.KEY_BATTERY_LEVEL, readValue(buf, length, false)); + position.set(Position.KEY_BATTERY_LEVEL, readValue(buf, length)); break; case 2: - position.set("usbConnected", readValue(buf, length, false) == 1); + position.set("usbConnected", readValue(buf, length) == 1); break; case 5: - position.set("uptime", readValue(buf, length, false)); + position.set("uptime", readValue(buf, length)); break; case 20: - position.set(Position.KEY_HDOP, readValue(buf, length, false) * 0.1); + position.set(Position.KEY_HDOP, readValue(buf, length) * 0.1); break; case 21: - position.set(Position.KEY_VDOP, readValue(buf, length, false) * 0.1); + position.set(Position.KEY_VDOP, readValue(buf, length) * 0.1); break; case 22: - position.set(Position.KEY_PDOP, readValue(buf, length, false) * 0.1); + position.set(Position.KEY_PDOP, readValue(buf, length) * 0.1); break; case 67: - position.set(Position.KEY_BATTERY, readValue(buf, length, false) * 0.001); + position.set(Position.KEY_BATTERY, readValue(buf, length) * 0.001); break; case 221: - position.set("button", readValue(buf, length, false)); + position.set("button", readValue(buf, length)); break; case 222: - if (readValue(buf, length, false) == 1) { + if (readValue(buf, length) == 1) { position.set(Position.KEY_ALARM, Position.ALARM_SOS); } break; case 240: - position.set(Position.KEY_MOTION, readValue(buf, length, false) == 1); + position.set(Position.KEY_MOTION, readValue(buf, length) == 1); break; case 244: - position.set(Position.KEY_ROAMING, readValue(buf, length, false) == 1); + position.set(Position.KEY_ROAMING, readValue(buf, length) == 1); break; default: - position.set(Position.PREFIX_IO + id, readValue(buf, length, false)); + position.set(Position.PREFIX_IO + id, readValue(buf, length)); break; } } - private void decodeParameter(Position position, int id, ByteBuf buf, int length, int codec) { + private void decodeParameter(Position position, int id, ByteBuf buf, int length, int codec, String model) { if (codec == CODEC_GH3000) { decodeGh3000Parameter(position, id, buf, length); } else { - decodeOtherParameter(position, id, buf, length); + int index = buf.readerIndex(); + boolean decoded = false; + for (var entry : PARAMETERS.getOrDefault(id, new HashMap<>()).entrySet()) { + if (entry.getKey() == null || model != null && entry.getKey().contains(model)) { + entry.getValue().accept(position, buf); + decoded = true; + break; + } + } + if (decoded) { + buf.readerIndex(index + length); + } else { + position.set(Position.PREFIX_IO + id, readValue(buf, length)); + } } } - private void decodeNetwork(Position position) { - long cid = position.getLong(Position.PREFIX_IO + 205); - int lac = position.getInteger(Position.PREFIX_IO + 206); - if (cid != 0 && lac != 0) { - CellTower cellTower = CellTower.fromLacCid(lac, cid); - long operator = position.getInteger(Position.KEY_OPERATOR); - if (operator >= 1000) { - cellTower.setOperator(operator); + private void decodeCell( + Position position, Network network, String mncKey, String lacKey, String cidKey, String rssiKey) { + if (position.hasAttribute(mncKey) && position.hasAttribute(lacKey) && position.hasAttribute(cidKey)) { + CellTower cellTower = CellTower.from( + getConfig().getInteger(Keys.GEOLOCATION_MCC), + ((Number) position.getAttributes().remove(mncKey)).intValue(), + ((Number) position.getAttributes().remove(lacKey)).intValue(), + ((Number) position.getAttributes().remove(cidKey)).longValue()); + cellTower.setSignalStrength(((Number) position.getAttributes().remove(rssiKey)).intValue()); + network.addCellTower(cellTower); + } + } + + private void decodeNetwork(Position position, String model) { + if ("TAT100".equals(model)) { + Network network = new Network(); + decodeCell(position, network, "io1200", "io287", "io288", "io289"); + decodeCell(position, network, "io1201", "io290", "io291", "io292"); + decodeCell(position, network, "io1202", "io293", "io294", "io295"); + decodeCell(position, network, "io1203", "io296", "io297", "io298"); + if (network.getCellTowers() != null) { + position.setNetwork(network); + } + } else { + Integer cid2g = (Integer) position.getAttributes().remove("cid2g"); + Long cid4g = (Long) position.getAttributes().remove("cid4g"); + Integer lac = (Integer) position.getAttributes().remove("lac"); + if (lac != null && (cid2g != null || cid4g != null)) { + Network network = new Network(); + CellTower cellTower; + if (cid2g != null) { + cellTower = CellTower.fromLacCid(getConfig(), lac, cid2g); + } else { + cellTower = CellTower.fromLacCid(getConfig(), lac, cid4g); + network.setRadioType("lte"); + } + long operator = position.getInteger(Position.KEY_OPERATOR); + if (operator >= 1000) { + cellTower.setOperator(operator); + } + network.addCellTower(cellTower); + position.setNetwork(new Network(cellTower)); } - position.setNetwork(new Network(cellTower)); } } @@ -384,7 +419,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { } } - private void decodeLocation(Position position, ByteBuf buf, int codec) { + private void decodeLocation(Position position, ByteBuf buf, int codec, String model) { int globalMask = 0x0f; @@ -422,7 +457,8 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { } if (BitUtil.check(locationMask, 5)) { - CellTower cellTower = CellTower.fromLacCid(buf.readUnsignedShort(), buf.readUnsignedShort()); + CellTower cellTower = CellTower.fromLacCid( + getConfig(), buf.readUnsignedShort(), buf.readUnsignedShort()); if (BitUtil.check(locationMask, 6)) { cellTower.setSignalStrength((int) buf.readUnsignedByte()); @@ -480,7 +516,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { if (BitUtil.check(globalMask, 1)) { int cnt = readExtByte(buf, codec, CODEC_8_EXT); for (int j = 0; j < cnt; j++) { - decodeParameter(position, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16), buf, 1, codec); + decodeParameter(position, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16), buf, 1, codec, model); } } @@ -488,7 +524,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { if (BitUtil.check(globalMask, 2)) { int cnt = readExtByte(buf, codec, CODEC_8_EXT); for (int j = 0; j < cnt; j++) { - decodeParameter(position, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16), buf, 2, codec); + decodeParameter(position, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16), buf, 2, codec, model); } } @@ -496,7 +532,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { if (BitUtil.check(globalMask, 3)) { int cnt = readExtByte(buf, codec, CODEC_8_EXT); for (int j = 0; j < cnt; j++) { - decodeParameter(position, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16), buf, 4, codec); + decodeParameter(position, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16), buf, 4, codec, model); } } @@ -504,7 +540,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { if (codec == CODEC_8 || codec == CODEC_8_EXT || codec == CODEC_16) { int cnt = readExtByte(buf, codec, CODEC_8_EXT); for (int j = 0; j < cnt; j++) { - decodeOtherParameter(position, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16), buf, 8); + decodeParameter(position, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16), buf, 8, codec, model); } } @@ -558,7 +594,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { } } - decodeNetwork(position); + decodeNetwork(position, model); } @@ -574,10 +610,10 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { int count = buf.readUnsignedByte(); DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei); - if (deviceSession == null) { return null; } + String model = getCacheManager().getObject(Device.class, deviceSession.getDeviceId()).getModel(); for (int i = 0; i < count; i++) { Position position = new Position(getProtocolName()); @@ -589,17 +625,21 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedByte(); // type int length = buf.readInt() - 4; getLastLocation(position, new Date(buf.readUnsignedInt() * 1000)); - if (isPrintable(buf, length)) { - position.set(Position.KEY_RESULT, - buf.readCharSequence(length, StandardCharsets.US_ASCII).toString().trim()); + if (BufferUtil.isPrintable(buf, length)) { + String data = buf.readCharSequence(length, StandardCharsets.US_ASCII).toString().trim(); + if (data.startsWith("GTSL")) { + position.set(Position.KEY_DRIVER_UNIQUE_ID, data.split("\\|")[4]); + } else { + position.set(Position.KEY_RESULT, data); + } } else { position.set(Position.KEY_RESULT, ByteBufUtil.hexDump(buf.readSlice(length))); } } else if (codec == CODEC_12) { - decodeSerial(channel, remoteAddress, position, buf); + decodeSerial(channel, remoteAddress, deviceSession, position, buf); } else { - decodeLocation(position, buf, codec); + decodeLocation(position, buf, codec, model); } if (!position.getOutdated() || !position.getAttributes().isEmpty()) { @@ -636,9 +676,11 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { } } - private Object decodeTcp(Channel channel, SocketAddress remoteAddress, ByteBuf buf) throws Exception { + private Object decodeTcp(Channel channel, SocketAddress remoteAddress, ByteBuf buf) { - if (buf.getUnsignedShort(0) > 0) { + if (buf.readableBytes() == 1 && buf.readUnsignedByte() == 0xff) { + return null; + } else if (buf.getUnsignedShort(0) > 0) { parseIdentification(channel, remoteAddress, buf); } else { buf.skipBytes(4); @@ -648,7 +690,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { return null; } - private Object decodeUdp(Channel channel, SocketAddress remoteAddress, ByteBuf buf) throws Exception { + private Object decodeUdp(Channel channel, SocketAddress remoteAddress, ByteBuf buf) { buf.readUnsignedShort(); // length buf.readUnsignedShort(); // packet id diff --git a/src/main/java/org/traccar/protocol/TeraTrackProtocol.java b/src/main/java/org/traccar/protocol/TeraTrackProtocol.java index 0303b4b5a..e872ddf42 100644 --- a/src/main/java/org/traccar/protocol/TeraTrackProtocol.java +++ b/src/main/java/org/traccar/protocol/TeraTrackProtocol.java @@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class TeraTrackProtocol extends BaseProtocol { - public TeraTrackProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public TeraTrackProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new JsonFrameDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/TeraTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/TeraTrackProtocolDecoder.java index c36da2aed..be4b98e4c 100644 --- a/src/main/java/org/traccar/protocol/TeraTrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TeraTrackProtocolDecoder.java @@ -17,14 +17,14 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; -import javax.json.Json; -import javax.json.JsonObject; +import jakarta.json.Json; +import jakarta.json.JsonObject; import java.io.StringReader; import java.net.SocketAddress; import java.text.DateFormat; @@ -63,7 +63,7 @@ public class TeraTrackProtocolDecoder extends BaseProtocolDecoder { position.setSpeed(UnitsConverter.knotsFromKph(Integer.parseInt(json.getString("Speed")))); position.set(Position.KEY_ODOMETER, Integer.parseInt(json.getString("Mileage"))); - position.set(Position.KEY_BLOCKED, json.getString("LockOpen").equals("0")); + position.set(Position.KEY_LOCK, json.getString("LockOpen").equals("0")); position.set(Position.KEY_DRIVER_UNIQUE_ID, json.getString("CardNo")); position.set(Position.KEY_ALARM, json.getString("LowPower").equals("1") ? Position.ALARM_LOW_POWER : null); position.set(Position.KEY_BATTERY_LEVEL, Integer.parseInt(json.getString("Power"))); diff --git a/src/main/java/org/traccar/protocol/ThinkPowerProtocol.java b/src/main/java/org/traccar/protocol/ThinkPowerProtocol.java index ece1b0544..b23dadf08 100644 --- a/src/main/java/org/traccar/protocol/ThinkPowerProtocol.java +++ b/src/main/java/org/traccar/protocol/ThinkPowerProtocol.java @@ -19,13 +19,17 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class ThinkPowerProtocol extends BaseProtocol { - public ThinkPowerProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public ThinkPowerProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 2, 2, 2, 0)); pipeline.addLast(new ThinkPowerProtocolDecoder(ThinkPowerProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/ThinkPowerProtocolDecoder.java b/src/main/java/org/traccar/protocol/ThinkPowerProtocolDecoder.java index b3f943078..e7ab23e5b 100644 --- a/src/main/java/org/traccar/protocol/ThinkPowerProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/ThinkPowerProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,8 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.helper.BufferUtil; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.Checksum; @@ -64,8 +65,8 @@ public class ThinkPowerProtocolDecoder extends BaseProtocolDecoder { switch (type) { case 0x01: position.setValid(true); - position.setLatitude(buf.readInt() * 0.0000001); - position.setLongitude(buf.readInt() * 0.0000001); + position.setLatitude(BufferUtil.readSignedMagnitudeInt(buf) * 0.0000001); + position.setLongitude(BufferUtil.readSignedMagnitudeInt(buf) * 0.0000001); position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort() * 0.1)); position.setCourse(buf.readUnsignedShort() * 0.01); break; diff --git a/src/main/java/org/traccar/protocol/ThinkRaceProtocol.java b/src/main/java/org/traccar/protocol/ThinkRaceProtocol.java index ca1237cef..34b80ba87 100644 --- a/src/main/java/org/traccar/protocol/ThinkRaceProtocol.java +++ b/src/main/java/org/traccar/protocol/ThinkRaceProtocol.java @@ -19,13 +19,17 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class ThinkRaceProtocol extends BaseProtocol { - public ThinkRaceProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public ThinkRaceProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 2 + 12 + 1 + 1, 2, 2, 0)); pipeline.addLast(new ThinkRaceProtocolDecoder(ThinkRaceProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/ThinkRaceProtocolDecoder.java b/src/main/java/org/traccar/protocol/ThinkRaceProtocolDecoder.java index 0928b25e0..796b726ea 100644 --- a/src/main/java/org/traccar/protocol/ThinkRaceProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/ThinkRaceProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; @@ -104,7 +104,7 @@ public class ThinkRaceProtocolDecoder extends BaseProtocolDecoder { position.setCourse(buf.readUnsignedByte()); position.setNetwork(new Network( - CellTower.fromLacCid(buf.readUnsignedShort(), buf.readUnsignedShort()))); + CellTower.fromLacCid(getConfig(), buf.readUnsignedShort(), buf.readUnsignedShort()))); return position; diff --git a/src/main/java/org/traccar/protocol/ThurayaProtocol.java b/src/main/java/org/traccar/protocol/ThurayaProtocol.java new file mode 100644 index 000000000..33d486f6b --- /dev/null +++ b/src/main/java/org/traccar/protocol/ThurayaProtocol.java @@ -0,0 +1,39 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.handler.codec.LengthFieldBasedFrameDecoder; +import org.traccar.BaseProtocol; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; + +public class ThurayaProtocol extends BaseProtocol { + + @Inject + public ThurayaProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 2, 2, -4, 0)); + pipeline.addLast(new ThurayaProtocolDecoder(ThurayaProtocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/ThurayaProtocolDecoder.java b/src/main/java/org/traccar/protocol/ThurayaProtocolDecoder.java new file mode 100644 index 000000000..a287ece34 --- /dev/null +++ b/src/main/java/org/traccar/protocol/ThurayaProtocolDecoder.java @@ -0,0 +1,195 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.NetworkMessage; +import org.traccar.Protocol; +import org.traccar.helper.BitUtil; +import org.traccar.helper.DateBuilder; +import org.traccar.model.Position; +import org.traccar.session.DeviceSession; + +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.LinkedList; +import java.util.List; + +public class ThurayaProtocolDecoder extends BaseProtocolDecoder { + + public ThurayaProtocolDecoder(Protocol protocol) { + super(protocol); + } + + public static final int MSG_EVENT = 0x5101; + public static final int MSG_PERIODIC_REPORT = 0x7101; + public static final int MSG_SETTING_RESPONSE = 0x8115; + public static final int MSG_ACK = 0x9901; + + private static int checksum(ByteBuffer buf) { + int crc = 0; + while (buf.hasRemaining()) { + crc += buf.get(); + } + crc = ~crc; + crc += 1; + return crc; + } + + private void sendResponse(Channel channel, SocketAddress remoteAddress, long id, int type) { + if (channel != null) { + ByteBuf response = Unpooled.buffer(); + response.writeCharSequence("#T", StandardCharsets.US_ASCII); + response.writeShort(15); // length + response.writeShort(MSG_ACK); + response.writeInt((int) id); + response.writeShort(type); + response.writeShort(1); // server ok + response.writeShort(checksum(response.nioBuffer())); + channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); + } + } + + private void decodeLocation(ByteBuf buf, Position position) { + + position.setValid(true); + + DateBuilder dateBuilder = new DateBuilder(); + + int date = buf.readInt(); + dateBuilder.setDay(date % 100); + date /= 100; + dateBuilder.setMonth(date % 100); + date /= 100; + dateBuilder.setYear(date); + + int time = buf.readInt(); + dateBuilder.setSecond(time % 100); + time /= 100; + dateBuilder.setMinute(time % 100); + time /= 100; + dateBuilder.setHour(time); + + position.setTime(dateBuilder.getDate()); + + position.setLongitude(buf.readInt() / 1000000.0); + position.setLatitude(buf.readInt() / 1000000.0); + + int data = buf.readUnsignedShort(); + + int ignition = BitUtil.from(data, 12); + if (ignition == 1) { + position.set(Position.KEY_IGNITION, true); + } else if (ignition == 2) { + position.set(Position.KEY_IGNITION, false); + } + + position.setCourse(BitUtil.to(data, 12)); + position.setSpeed(buf.readShort()); + + position.set(Position.KEY_RPM, buf.readShort()); + + position.set("data", readString(buf)); + } + + private String decodeAlarm(int event) { + switch (event) { + case 10: + return Position.ALARM_VIBRATION; + case 11: + return Position.ALARM_OVERSPEED; + case 12: + return Position.ALARM_POWER_CUT; + case 13: + return Position.ALARM_LOW_BATTERY; + case 18: + return Position.ALARM_GPS_ANTENNA_CUT; + case 20: + return Position.ALARM_ACCELERATION; + case 21: + return Position.ALARM_BRAKING; + default: + return null; + } + } + + private String readString(ByteBuf buf) { + int endIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 0); + CharSequence value = buf.readCharSequence(endIndex - buf.readerIndex(), StandardCharsets.US_ASCII); + buf.readUnsignedByte(); // delimiter + return value.toString(); + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + ByteBuf buf = (ByteBuf) msg; + + buf.skipBytes(2); // service + buf.readUnsignedShort(); // length + int type = buf.readUnsignedShort(); + long id = buf.readUnsignedInt(); + + sendResponse(channel, remoteAddress, id, type); + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(id)); + if (deviceSession == null) { + return null; + } + + if (type == MSG_EVENT) { + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + decodeLocation(buf, position); + + int event = buf.readUnsignedByte(); + position.set(Position.KEY_ALARM, decodeAlarm(event)); + position.set(Position.KEY_EVENT, event); + position.set("eventData", readString(buf)); + + return position; + + } else if (type == MSG_PERIODIC_REPORT) { + + List<Position> positions = new LinkedList<>(); + + int count = buf.readUnsignedByte(); + for (int i = 0; i < count; i++) { + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + decodeLocation(buf, position); + + positions.add(position); + + } + + return positions; + + } + + return null; + } + +} diff --git a/src/main/java/org/traccar/protocol/Tk102Protocol.java b/src/main/java/org/traccar/protocol/Tk102Protocol.java index 9f2463cd6..b6a82981b 100644 --- a/src/main/java/org/traccar/protocol/Tk102Protocol.java +++ b/src/main/java/org/traccar/protocol/Tk102Protocol.java @@ -19,13 +19,17 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class Tk102Protocol extends BaseProtocol { - public Tk102Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public Tk102Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 1 + 1 + 10, 1, 1, 0)); pipeline.addLast(new Tk102ProtocolDecoder(Tk102Protocol.this)); } diff --git a/src/main/java/org/traccar/protocol/Tk102ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tk102ProtocolDecoder.java index da0c6928b..af29fbc21 100644 --- a/src/main/java/org/traccar/protocol/Tk102ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Tk102ProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; diff --git a/src/main/java/org/traccar/protocol/Tk103Protocol.java b/src/main/java/org/traccar/protocol/Tk103Protocol.java index ff0bedfb7..b641ef083 100644 --- a/src/main/java/org/traccar/protocol/Tk103Protocol.java +++ b/src/main/java/org/traccar/protocol/Tk103Protocol.java @@ -21,11 +21,15 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class Tk103Protocol extends BaseProtocol { - public Tk103Protocol() { + @Inject + public Tk103Protocol(Config config) { setSupportedDataCommands( Command.TYPE_CUSTOM, Command.TYPE_GET_DEVICE_STATUS, @@ -45,9 +49,9 @@ public class Tk103Protocol extends BaseProtocol { Command.TYPE_ENGINE_STOP, Command.TYPE_ENGINE_RESUME, Command.TYPE_OUTPUT_CONTROL); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new Tk103FrameDecoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); @@ -55,9 +59,9 @@ public class Tk103Protocol extends BaseProtocol { pipeline.addLast(new Tk103ProtocolDecoder(Tk103Protocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new Tk103ProtocolEncoder(Tk103Protocol.this)); diff --git a/src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java index ff33cb103..6c926da90 100644 --- a/src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 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. @@ -15,10 +15,12 @@ */ package org.traccar.protocol; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; +import org.traccar.helper.DataConverter; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.config.Keys; @@ -36,11 +38,15 @@ import java.util.regex.Pattern; public class Tk103ProtocolDecoder extends BaseProtocolDecoder { - private final boolean decodeLow; + private boolean decodeLow; public Tk103ProtocolDecoder(Protocol protocol) { super(protocol); - decodeLow = Context.getConfig().getBoolean(Keys.PROTOCOL_DECODE_LOW.withPrefix(getProtocolName())); + } + + @Override + protected void init() { + decodeLow = getConfig().getBoolean(Keys.PROTOCOL_DECODE_LOW.withPrefix(getProtocolName())); } private static final Pattern PATTERN = new PatternBuilder() @@ -96,6 +102,16 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder { .any() .compile(); + private static final Pattern PATTERN_CELL = new PatternBuilder() + .text("(") + .number("(d{12})") // device id + .expression(".{4}") // type + .number("(?:d{15})?,") // imei + .expression("(.+),") // cell + .number("(d{8})") // odometer + .text(")") + .compile(); + private static final Pattern PATTERN_NETWORK = new PatternBuilder() .text("(").optional() .number("(d{12})") // device id @@ -294,6 +310,39 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder { return position; } + private Position decodeCell(Channel channel, SocketAddress remoteAddress, String sentence) { + Parser parser = new Parser(PATTERN_CELL, sentence); + if (!parser.matches()) { + return null; + } + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + getLastLocation(position, null); + + Network network = new Network(); + + String[] cells = parser.next().split("\n"); + for (String cell : cells) { + String[] values = cell.substring(1, cell.length() - 1).split(","); + network.addCellTower(CellTower.from( + Integer.parseInt(values[0]), Integer.parseInt(values[1]), + Integer.parseInt(values[2]), Integer.parseInt(values[3]))); + } + + position.setNetwork(network); + + position.set(Position.KEY_ODOMETER, parser.nextLong(16, 0)); + + return position; + } + private Position decodeNetwork(Channel channel, SocketAddress remoteAddress, String sentence) { Parser parser = new Parser(PATTERN_NETWORK, sentence); if (!parser.matches()) { @@ -402,6 +451,106 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder { return position; } + private Position decodeBms(Channel channel, SocketAddress remoteAddress, String sentence) { + String id = sentence.substring(1, 13); + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + getLastLocation(position, null); + + String payload = sentence.substring(1 + 12 + 4, sentence.length() - 1); + + if (sentence.startsWith("BS50", 1 + 12)) { + + ByteBuf buf = Unpooled.wrappedBuffer(DataConverter.parseHex(payload)); + + buf.readUnsignedByte(); + buf.readUnsignedByte(); + buf.readUnsignedByte(); // header + + int batteryCount = buf.readUnsignedByte(); + for (int i = 1; i <= 24; i++) { + int voltage = buf.readUnsignedShortLE(); + if (i <= batteryCount) { + position.set("battery" + i, voltage * 0.001); + } + } + + position.set(Position.KEY_CHARGE, buf.readUnsignedByte() == 0); + position.set("current", buf.readUnsignedShortLE() * 0.1); + position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() * 0.01); + position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte()); + position.set("batteryOverheat", buf.readUnsignedByte() > 0); + position.set("chargeProtection", buf.readUnsignedByte() > 0); + position.set("dischargeProtection", buf.readUnsignedByte() > 0); + buf.readUnsignedByte(); // drop line + buf.readUnsignedByte(); // balanced + position.set("cycles", buf.readUnsignedShortLE()); + position.set("faultAlarm", buf.readUnsignedByte()); + + buf.skipBytes(6); + + int temperatureCount = buf.readUnsignedByte(); + position.set("powerTemp", buf.readUnsignedByte() - 40); + position.set("equilibriumTemp", buf.readUnsignedByte() - 40); + for (int i = 1; i <= 7; i++) { + int temperature = buf.readUnsignedByte() - 40; + if (i <= temperatureCount) { + position.set("batteryTemp" + i, temperature); + } + } + + position.set("calibrationCapacity", buf.readUnsignedShortLE() * 0.01); + position.set("dischargeCapacity", buf.readUnsignedIntLE()); + + } else { + + String[] values = payload.split(","); + for (String value : values) { + String[] pair = value.split(":"); + int key = Integer.parseInt(pair[0], 16); + ByteBuf buf = Unpooled.wrappedBuffer(DataConverter.parseHex(pair[1])); + switch (key) { + case 0x90: + position.set("cumulativeVoltage", buf.readUnsignedShortLE() * 0.1); + position.set("gatherVoltage", buf.readUnsignedShortLE() * 0.1); + position.set("current", (buf.readUnsignedShortLE() - 30000) * 0.1); + position.set("soc", buf.readUnsignedShortLE() * 0.1); + break; + case 0x91: + position.set("maxCellVoltage", buf.readUnsignedShortLE() * 0.001); + position.set("maxCellVoltageCount", buf.readUnsignedByte()); + position.set("minCellVoltage", buf.readUnsignedShortLE() * 0.001); + position.set("minCellVoltageCount", buf.readUnsignedByte()); + break; + case 0x92: + position.set("maxTemp", buf.readUnsignedByte() - 40); + position.set("maxTempCount", buf.readUnsignedByte()); + position.set("minTemp", buf.readUnsignedByte() - 40); + position.set("minTempCount", buf.readUnsignedByte()); + break; + case 0x96: + buf.readUnsignedByte(); // frame + while (buf.isReadable()) { + position.set("cellTemp" + buf.readerIndex(), buf.readUnsignedByte() - 40); + } + break; + default: + break; + } + + } + + } + + return position; + } + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { @@ -419,7 +568,9 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder { } } - if (sentence.contains("ZC20")) { + if (sentence.indexOf('{') > 0 && sentence.indexOf('}') > 0) { + return decodeCell(channel, remoteAddress, sentence); + } else if (sentence.contains("ZC20")) { return decodeBattery(channel, remoteAddress, sentence); } else if (sentence.contains("BZ00")) { return decodeNetwork(channel, remoteAddress, sentence); @@ -429,6 +580,8 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder { return decodeLbsWifi(channel, remoteAddress, sentence); } else if (sentence.contains("BV00")) { return decodeVin(channel, remoteAddress, sentence); + } else if (sentence.contains("BS50") || sentence.contains("BS51")) { + return decodeBms(channel, remoteAddress, sentence); } Parser parser = new Parser(PATTERN, sentence); diff --git a/src/main/java/org/traccar/protocol/Tk103ProtocolEncoder.java b/src/main/java/org/traccar/protocol/Tk103ProtocolEncoder.java index 5d7e63920..e3e1ae961 100644 --- a/src/main/java/org/traccar/protocol/Tk103ProtocolEncoder.java +++ b/src/main/java/org/traccar/protocol/Tk103ProtocolEncoder.java @@ -16,10 +16,11 @@ */ package org.traccar.protocol; -import org.traccar.Context; +import org.traccar.Protocol; import org.traccar.StringProtocolEncoder; +import org.traccar.config.Keys; +import org.traccar.helper.model.AttributeUtil; import org.traccar.model.Command; -import org.traccar.Protocol; public class Tk103ProtocolEncoder extends StringProtocolEncoder { @@ -42,12 +43,12 @@ public class Tk103ProtocolEncoder extends StringProtocolEncoder { @Override protected Object encodeCommand(Command command) { - boolean alternative = forceAlternative || Context.getIdentityManager().lookupAttributeBoolean( - command.getDeviceId(), getProtocolName() + ".alternative", false, false, true); + boolean alternative = AttributeUtil.lookup( + getCacheManager(), Keys.PROTOCOL_ALTERNATIVE.withPrefix(getProtocolName()), command.getDeviceId()); initDevicePassword(command, "123456"); - if (alternative) { + if (alternative || forceAlternative) { switch (command.getType()) { case Command.TYPE_CUSTOM: return formatAlt(command, "%s", Command.KEY_DATA); diff --git a/src/main/java/org/traccar/protocol/Tlt2hProtocol.java b/src/main/java/org/traccar/protocol/Tlt2hProtocol.java index 12fd92afa..6763e9b6b 100644 --- a/src/main/java/org/traccar/protocol/Tlt2hProtocol.java +++ b/src/main/java/org/traccar/protocol/Tlt2hProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class Tlt2hProtocol extends BaseProtocol { - public Tlt2hProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public Tlt2hProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(32 * 1024, "##\r\n")); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java index ad7dfa886..e85bdf9b3 100644 --- a/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,8 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.model.CellTower; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; @@ -55,6 +56,12 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder { private static final Pattern PATTERN_POSITION = new PatternBuilder() .text("#") .number("(?:(dd)|x*)") // cell or voltage + .groupBegin() + .number("#(d+),") // mcc + .number("(d+),") // mnc + .number("(x+),") // lac + .number("(x+)") // cell id + .groupEnd("?") .text("$GPRMC,") .number("(dd)(dd)(dd).d+,") // time (hhmmss.sss) .expression("([AVL]),") // validity @@ -71,6 +78,12 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder { private static final Pattern PATTERN_WIFI = new PatternBuilder() .text("#") .number("(?:(dd)|x+)") // cell or voltage + .groupBegin() + .number("#(d+),") // mcc + .number("(d+),") // mnc + .number("(x+),") // lac + .number("(x+)") // cell id + .groupEnd("?") .text("$WIFI,") .number("(dd)(dd)(dd).d+,") // time (hhmmss.sss) .expression("[AVL],") // validity @@ -168,6 +181,13 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_BATTERY, parser.nextInt() * 0.1); } + if (parser.hasNext(4)) { + Network network = new Network(); + network.addCellTower(CellTower.from( + parser.nextInt(), parser.nextInt(), parser.nextHexInt(), parser.nextHexInt())); + position.setNetwork(network); + } + DateBuilder dateBuilder = new DateBuilder() .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); @@ -191,11 +211,16 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_BATTERY, parser.nextInt() * 0.1); + Network network = new Network(); + if (parser.hasNext(4)) { + network.addCellTower(CellTower.from( + parser.nextInt(), parser.nextInt(), parser.nextHexInt(), parser.nextHexInt())); + } + DateBuilder dateBuilder = new DateBuilder() .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); String[] values = parser.next().split(","); - Network network = new Network(); for (int i = 0; i < values.length / 2; i++) { String mac = values[i * 2 + 1].replaceAll("(..)", "$1:").substring(0, 17); network.addWifiAccessPoint(WifiAccessPoint.from(mac, Integer.parseInt(values[i * 2]))); diff --git a/src/main/java/org/traccar/protocol/TlvProtocol.java b/src/main/java/org/traccar/protocol/TlvProtocol.java index 94f5da94f..f99676d23 100644 --- a/src/main/java/org/traccar/protocol/TlvProtocol.java +++ b/src/main/java/org/traccar/protocol/TlvProtocol.java @@ -19,13 +19,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class TlvProtocol extends BaseProtocol { - public TlvProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public TlvProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '\0')); pipeline.addLast(new TlvProtocolDecoder(TlvProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/TlvProtocolDecoder.java b/src/main/java/org/traccar/protocol/TlvProtocolDecoder.java index 36cf7859f..7870c778a 100644 --- a/src/main/java/org/traccar/protocol/TlvProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TlvProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.UnitsConverter; diff --git a/src/main/java/org/traccar/protocol/TmgProtocol.java b/src/main/java/org/traccar/protocol/TmgProtocol.java index 020332ce7..dbba648be 100644 --- a/src/main/java/org/traccar/protocol/TmgProtocol.java +++ b/src/main/java/org/traccar/protocol/TmgProtocol.java @@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class TmgProtocol extends BaseProtocol { - public TmgProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public TmgProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new TmgFrameDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/TmgProtocolDecoder.java b/src/main/java/org/traccar/protocol/TmgProtocolDecoder.java index d27849f8c..00dc2a09b 100644 --- a/src/main/java/org/traccar/protocol/TmgProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TmgProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.BitUtil; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/TopflytechProtocol.java b/src/main/java/org/traccar/protocol/TopflytechProtocol.java index 303072bdb..a658235ab 100644 --- a/src/main/java/org/traccar/protocol/TopflytechProtocol.java +++ b/src/main/java/org/traccar/protocol/TopflytechProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class TopflytechProtocol extends BaseProtocol { - public TopflytechProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public TopflytechProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ')')); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/TopflytechProtocolDecoder.java b/src/main/java/org/traccar/protocol/TopflytechProtocolDecoder.java index 6de053c32..92a7b5c9d 100644 --- a/src/main/java/org/traccar/protocol/TopflytechProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TopflytechProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/TopinProtocol.java b/src/main/java/org/traccar/protocol/TopinProtocol.java index d28afbf94..1a558f617 100644 --- a/src/main/java/org/traccar/protocol/TopinProtocol.java +++ b/src/main/java/org/traccar/protocol/TopinProtocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2019 - 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. @@ -18,16 +18,23 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; +import org.traccar.config.Keys; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class TopinProtocol extends BaseProtocol { - public TopinProtocol() { - setSupportedDataCommands( - Command.TYPE_SOS_NUMBER); - addServer(new TrackerServer(false, getName()) { + @Inject + public TopinProtocol(Config config) { + if (!config.getBoolean(Keys.PROTOCOL_DISABLE_COMMANDS.withPrefix(getName()))) { + setSupportedDataCommands( + Command.TYPE_SOS_NUMBER); + } + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new TopinProtocolEncoder(TopinProtocol.this)); pipeline.addLast(new TopinProtocolDecoder(TopinProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java b/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java index 4fe261aa4..b5dd3c4b9 100644 --- a/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java @@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BcdUtil; @@ -48,7 +48,11 @@ public class TopinProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_GPS = 0x10; public static final int MSG_GPS_OFFLINE = 0x11; public static final int MSG_STATUS = 0x13; + public static final int MSG_SLEEP = 0x14; + public static final int MSG_FACTORY_RESET = 0x15; public static final int MSG_WIFI_OFFLINE = 0x17; + public static final int MSG_LBS_WIFI = 0x18; + public static final int MSG_LBS_WIFI_OFFLINE = 0x19; public static final int MSG_TIME_UPDATE = 0x30; public static final int MSG_SOS_NUMBER = 0x41; public static final int MSG_WIFI = 0x69; @@ -216,7 +220,8 @@ public class TopinProtocolDecoder extends BaseProtocolDecoder { return position; - } else if (type == MSG_WIFI || type == MSG_WIFI_OFFLINE) { + } else if (type == MSG_WIFI || type == MSG_WIFI_OFFLINE + || type == MSG_LBS_WIFI || type == MSG_LBS_WIFI_OFFLINE) { Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); diff --git a/src/main/java/org/traccar/protocol/TotemProtocol.java b/src/main/java/org/traccar/protocol/TotemProtocol.java index 417a4d6b0..b02d4f1fc 100644 --- a/src/main/java/org/traccar/protocol/TotemProtocol.java +++ b/src/main/java/org/traccar/protocol/TotemProtocol.java @@ -20,11 +20,15 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class TotemProtocol extends BaseProtocol { - public TotemProtocol() { + @Inject + public TotemProtocol(Config config) { setSupportedDataCommands( Command.TYPE_CUSTOM, Command.TYPE_REBOOT_DEVICE, @@ -46,9 +50,9 @@ public class TotemProtocol extends BaseProtocol { Command.TYPE_ENGINE_STOP ); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new TotemFrameDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/TotemProtocolDecoder.java b/src/main/java/org/traccar/protocol/TotemProtocolDecoder.java index 58c66031e..6f039c324 100644 --- a/src/main/java/org/traccar/protocol/TotemProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TotemProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 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. @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; @@ -135,13 +135,17 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { private static final Pattern PATTERN4 = new PatternBuilder() .text("$$") // header .number("dddd") // length - .number("(xx)") // type + .number("xx") // type .number("(d+)|") // imei .number("(x{8})") // status .number("(dd)(dd)(dd)") // date (yymmdd) .number("(dd)(dd)(dd)") // time (hhmmss) + .groupBegin() .number("(dd)") // battery .number("(dd)") // external power + .or() + .number("(ddd)") // battery + .groupEnd() .number("(dddd)") // adc 1 .groupBegin() .groupBegin() @@ -166,12 +170,27 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { .number("(d{7})") // odometer .number("(dd)(dd.dddd)([NS])") // latitude .number("(ddd)(dd.dddd)([EW])") // longitude + .number("dddd").optional() // temperature .number("dddd") // serial number .number("xx") // checksum .any() .compile(); - private static final Pattern PATTERN_OBD = new PatternBuilder() + private static final Pattern PATTERN_E2 = new PatternBuilder() + .text("$$") // header + .number("dddd") // length + .number("xx") // type + .number("(d+)|") // imei + .number("(dd)(dd)(dd)") // date (yymmdd) + .number("(dd)(dd)(dd),") // time (hhmmss) + .number("(-?d+.d+),") // longitude + .number("(-?d+.d+),") // latitude + .expression("(.+)") // rfid + .number("|xx") // checksum + .any() + .compile(); + + private static final Pattern PATTERN_E5 = new PatternBuilder() .text("$$") // header .number("dddd") // length .number("xx") // type @@ -252,7 +271,20 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { } } - private boolean decode12(Position position, Parser parser, Pattern pattern) { + private Position decode12(Channel channel, SocketAddress remoteAddress, String sentence, Pattern pattern) { + + Parser parser = new Parser(pattern, sentence); + if (!parser.matches()) { + return null; + } + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); if (parser.hasNext()) { position.set(Position.KEY_ALARM, decodeAlarm123(Short.parseShort(parser.next(), 16))); @@ -278,7 +310,7 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { year = parser.nextInt(0); } if (year == 0) { - return false; // ignore invalid data + return null; // ignore invalid data } dateBuilder.setDate(year, month, day); position.setTime(dateBuilder.getDate()); @@ -320,16 +352,29 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { int lac = parser.nextHexInt(0); int cid = parser.nextHexInt(0); if (lac != 0 && cid != 0) { - position.setNetwork(new Network(CellTower.fromLacCid(lac, cid))); + position.setNetwork(new Network(CellTower.fromLacCid(getConfig(), lac, cid))); } position.set(Position.PREFIX_TEMP + 1, parser.next()); position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); - return true; + return position; } - private boolean decode3(Position position, Parser parser) { + private Position decode3(Channel channel, SocketAddress remoteAddress, String sentence) { + + Parser parser = new Parser(PATTERN3, sentence); + if (!parser.matches()) { + return null; + } + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); if (parser.hasNext()) { position.set(Position.KEY_ALARM, decodeAlarm123(Short.parseShort(parser.next(), 16))); @@ -346,7 +391,7 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { position.set(Position.PREFIX_TEMP + 2, parser.next()); position.setNetwork(new Network( - CellTower.fromLacCid(parser.nextHexInt(0), parser.nextHexInt(0)))); + CellTower.fromLacCid(getConfig(), parser.nextHexInt(0), parser.nextHexInt(0)))); position.setValid(parser.next().equals("A")); position.set(Position.KEY_SATELLITES, parser.nextInt()); @@ -358,10 +403,36 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { position.setLatitude(parser.nextCoordinate()); position.setLongitude(parser.nextCoordinate()); - return true; + return position; } - private boolean decode4(Position position, Parser parser) { + private Position decode4(Channel channel, SocketAddress remoteAddress, String sentence) { + + int type = Integer.parseInt(sentence.substring(6, 8), 16); + + switch (type) { + case 0xE2: + return decodeE2(channel, remoteAddress, sentence); + case 0xE5: + return decodeE5(channel, remoteAddress, sentence); + default: + break; + } + + Parser parser = new Parser(PATTERN4, sentence); + if (!parser.matches()) { + return null; + } + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.set(Position.KEY_ALARM, decodeAlarm4(type)); long status = parser.nextHexLong(); @@ -379,8 +450,13 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { position.setTime(parser.nextDateTime()); - position.set(Position.KEY_BATTERY, parser.nextDouble() * 0.1); - position.set(Position.KEY_POWER, parser.nextDouble()); + if (parser.hasNext(2)) { + position.set(Position.KEY_BATTERY, parser.nextDouble() * 0.1); + position.set(Position.KEY_POWER, parser.nextDouble()); + } + if (parser.hasNext()) { + position.set(Position.KEY_BATTERY, parser.nextDouble() * 0.01); + } position.set(Position.PREFIX_ADC + 1, parser.next()); position.set(Position.PREFIX_ADC + 2, parser.next()); @@ -403,7 +479,7 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { int mcc = parser.nextInt(); cellTower = CellTower.from(mcc, mnc, lac, cid); } else { - cellTower = CellTower.fromLacCid(lac, cid); + cellTower = CellTower.fromLacCid(getConfig(), lac, cid); } position.set(Position.KEY_SATELLITES, parser.nextInt()); cellTower.setSignalStrength(parser.nextInt()); @@ -417,10 +493,48 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { position.setLatitude(parser.nextCoordinate()); position.setLongitude(parser.nextCoordinate()); - return true; + return position; } - private boolean decodeObd(Position position, Parser parser) { + private Position decodeE2(Channel channel, SocketAddress remoteAddress, String sentence) { + + Parser parser = new Parser(PATTERN_E2, sentence); + if (!parser.matches()) { + return null; + } + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.setValid(true); + position.setTime(parser.nextDateTime()); + position.setLongitude(parser.nextDouble()); + position.setLatitude(parser.nextDouble()); + + position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next()); + + return position; + } + + private Position decodeE5(Channel channel, SocketAddress remoteAddress, String sentence) { + + Parser parser = new Parser(PATTERN_E5, sentence); + if (!parser.matches()) { + return null; + } + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); position.setValid(true); position.setTime(parser.nextDateTime()); @@ -441,7 +555,7 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_THROTTLE, parser.nextInt()); position.set(Position.KEY_FUEL_LEVEL, parser.nextInt()); - return true; + return position; } @Override @@ -449,50 +563,23 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { String sentence = (String) msg; - Pattern pattern = PATTERN3; - if (sentence.contains("$Cloud")) { - pattern = PATTERN_OBD; - } else if (sentence.charAt(2) == '0') { - pattern = PATTERN4; + + Position position; + if (sentence.charAt(2) == '0') { + position = decode4(channel, remoteAddress, sentence); } else if (sentence.contains("$GPRMC")) { - pattern = PATTERN1; + position = decode12(channel, remoteAddress, sentence, PATTERN1); } else { int index = sentence.indexOf('|'); if (index != -1 && sentence.indexOf('|', index + 1) != -1) { - pattern = PATTERN2; + position = decode12(channel, remoteAddress, sentence, PATTERN2); + } else { + position = decode3(channel, remoteAddress, sentence); } } - Parser parser = new Parser(pattern, sentence); - if (!parser.matches()) { - return null; - } - - Position position = new Position(getProtocolName()); - - if (pattern == PATTERN4) { - position.set(Position.KEY_ALARM, decodeAlarm4(parser.nextHexInt())); - } - - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); - if (deviceSession == null) { - return null; - } - position.setDeviceId(deviceSession.getDeviceId()); - - boolean result; - if (pattern == PATTERN1 || pattern == PATTERN2) { - result = decode12(position, parser, pattern); - } else if (pattern == PATTERN3) { - result = decode3(position, parser); - } else if (pattern == PATTERN4) { - result = decode4(position, parser); - } else { - result = decodeObd(position, parser); - } - if (channel != null) { - if (pattern == PATTERN4) { + if (sentence.charAt(2) == '0') { String response = "$$0014AA" + sentence.substring(sentence.length() - 6, sentence.length() - 2); response += String.format("%02X", Checksum.xor(response)).toUpperCase(); channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); @@ -501,7 +588,7 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { } } - return result ? position : null; + return position; } } diff --git a/src/main/java/org/traccar/protocol/Tr20Protocol.java b/src/main/java/org/traccar/protocol/Tr20Protocol.java index 1b71db03f..3b3fc02b6 100644 --- a/src/main/java/org/traccar/protocol/Tr20Protocol.java +++ b/src/main/java/org/traccar/protocol/Tr20Protocol.java @@ -21,22 +21,26 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class Tr20Protocol extends BaseProtocol { - public Tr20Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public Tr20Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new Tr20ProtocolDecoder(Tr20Protocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new Tr20ProtocolDecoder(Tr20Protocol.this)); diff --git a/src/main/java/org/traccar/protocol/Tr20ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tr20ProtocolDecoder.java index 2f11bd152..0e1c7568b 100644 --- a/src/main/java/org/traccar/protocol/Tr20ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Tr20ProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.Parser; @@ -52,7 +52,7 @@ public class Tr20ProtocolDecoder extends BaseProtocolDecoder { .number("(ddd)(dd.d+),") // longitude .number("(d+),") // speed .number("(d+),") // course - .number("(?:NA|[FC]?(-?d+)[^,]*),") // temperature + .number("(?:NA|[BFC]?(-?d+)[^,]*),") // temperature .number("(x{8}),") // status .number("(d+)") // event .any() diff --git a/src/main/java/org/traccar/protocol/Tr900Protocol.java b/src/main/java/org/traccar/protocol/Tr900Protocol.java index b70521b35..c5f357604 100644 --- a/src/main/java/org/traccar/protocol/Tr900Protocol.java +++ b/src/main/java/org/traccar/protocol/Tr900Protocol.java @@ -21,22 +21,26 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class Tr900Protocol extends BaseProtocol { - public Tr900Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public Tr900Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new Tr900ProtocolDecoder(Tr900Protocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new Tr900ProtocolDecoder(Tr900Protocol.this)); diff --git a/src/main/java/org/traccar/protocol/Tr900ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tr900ProtocolDecoder.java index 319194c21..da0e8d292 100644 --- a/src/main/java/org/traccar/protocol/Tr900ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Tr900ProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/TrackboxProtocol.java b/src/main/java/org/traccar/protocol/TrackboxProtocol.java index 5da5abd64..eadcd07f9 100644 --- a/src/main/java/org/traccar/protocol/TrackboxProtocol.java +++ b/src/main/java/org/traccar/protocol/TrackboxProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class TrackboxProtocol extends BaseProtocol { - public TrackboxProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public TrackboxProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/TrackboxProtocolDecoder.java b/src/main/java/org/traccar/protocol/TrackboxProtocolDecoder.java index db8022738..10483d445 100644 --- a/src/main/java/org/traccar/protocol/TrackboxProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TrackboxProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; diff --git a/src/main/java/org/traccar/protocol/TrakMateProtocol.java b/src/main/java/org/traccar/protocol/TrakMateProtocol.java index bda5df10f..f4e7c5e60 100644 --- a/src/main/java/org/traccar/protocol/TrakMateProtocol.java +++ b/src/main/java/org/traccar/protocol/TrakMateProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class TrakMateProtocol extends BaseProtocol { - public TrakMateProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public TrakMateProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '#')); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/TrakMateProtocolDecoder.java b/src/main/java/org/traccar/protocol/TrakMateProtocolDecoder.java index 4d5cb18f5..b1f50dc10 100644 --- a/src/main/java/org/traccar/protocol/TrakMateProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TrakMateProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/TramigoFrameDecoder.java b/src/main/java/org/traccar/protocol/TramigoFrameDecoder.java index e4c94dc77..72cadf21a 100644 --- a/src/main/java/org/traccar/protocol/TramigoFrameDecoder.java +++ b/src/main/java/org/traccar/protocol/TramigoFrameDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 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. @@ -29,9 +29,13 @@ public class TramigoFrameDecoder extends BaseFrameDecoder { return null; } + int protocol = buf.getUnsignedByte(buf.readerIndex()); + int length; - if (buf.getUnsignedByte(buf.readerIndex()) == 0x80) { + if (protocol == 0x80) { length = buf.getUnsignedShortLE(buf.readerIndex() + 6); + } else if (protocol == 0x02 || protocol == 0x04) { + length = buf.getUnsignedShortLE(buf.readerIndex() + 1); } else { length = buf.getUnsignedShort(buf.readerIndex() + 6); } diff --git a/src/main/java/org/traccar/protocol/TramigoProtocol.java b/src/main/java/org/traccar/protocol/TramigoProtocol.java index f683ccc5d..5d8baf2a9 100644 --- a/src/main/java/org/traccar/protocol/TramigoProtocol.java +++ b/src/main/java/org/traccar/protocol/TramigoProtocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class TramigoProtocol extends BaseProtocol { - public TramigoProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public TramigoProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new TramigoFrameDecoder()); pipeline.addLast(new TramigoProtocolDecoder(TramigoProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/TramigoProtocolDecoder.java b/src/main/java/org/traccar/protocol/TramigoProtocolDecoder.java index e42e2f670..4a9a9a58f 100644 --- a/src/main/java/org/traccar/protocol/TramigoProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TramigoProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2014 - 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. @@ -19,7 +19,9 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.helper.BitUtil; +import org.traccar.helper.Checksum; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.DateUtil; @@ -29,6 +31,7 @@ import org.traccar.model.Position; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; import java.text.DateFormat; +import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; @@ -42,9 +45,6 @@ public class TramigoProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - public static final int MSG_COMPACT = 0x0100; - public static final int MSG_FULL = 0x00FE; - private static final String[] DIRECTIONS = new String[] {"N", "NE", "E", "SE", "S", "SW", "W", "NW"}; @Override @@ -54,34 +54,47 @@ public class TramigoProtocolDecoder extends BaseProtocolDecoder { ByteBuf buf = (ByteBuf) msg; int protocol = buf.readUnsignedByte(); - boolean legacy = protocol == 0x80; + + if (protocol == 0x01) { + return decode01(channel, remoteAddress, buf); + } else if (protocol == 0x04) { + return decode04(channel, remoteAddress, buf); + } else if (protocol == 0x80) { + return decode80(channel, remoteAddress, buf); + } + + return null; + } + + private Position decode01(Channel channel, SocketAddress remoteAddress, ByteBuf buf) { buf.readUnsignedByte(); // version id - int index = legacy ? buf.readUnsignedShort() : buf.readUnsignedShortLE(); - int type = legacy ? buf.readUnsignedShort() : buf.readUnsignedShortLE(); - buf.readUnsignedShort(); // length - buf.readUnsignedShort(); // mask - buf.readUnsignedShort(); // checksum - long id = legacy ? buf.readUnsignedInt() : buf.readUnsignedIntLE(); - buf.readUnsignedInt(); // time + int index = buf.readUnsignedShortLE(); + int type = buf.readUnsignedShortLE(); - Position position = new Position(getProtocolName()); - position.set(Position.KEY_INDEX, index); - position.setValid(true); + if (type == 0x0100 || type == 0x00FE) { - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(id)); - if (deviceSession == null) { - return null; - } - position.setDeviceId(deviceSession.getDeviceId()); + buf.readUnsignedShort(); // length + buf.readUnsignedShort(); // mask + buf.readUnsignedShort(); // checksum + long id = buf.readUnsignedIntLE(); + buf.readUnsignedInt(); // time - if (protocol == 0x01 && (type == MSG_COMPACT || type == MSG_FULL)) { + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(id)); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + position.set(Position.KEY_INDEX, index); // need to send ack? buf.readUnsignedShortLE(); // report trigger buf.readUnsignedShortLE(); // state flag + position.setValid(true); position.setLatitude(buf.readUnsignedIntLE() * 0.0000001); position.setLongitude(buf.readUnsignedIntLE() * 0.0000001); @@ -105,56 +118,183 @@ public class TramigoProtocolDecoder extends BaseProtocolDecoder { return position; - } else if (legacy) { + } - if (channel != null) { - channel.writeAndFlush(new NetworkMessage( - Unpooled.copiedBuffer("gprs,ack," + index, StandardCharsets.US_ASCII), remoteAddress)); - } + return null; - String sentence = buf.toString(StandardCharsets.US_ASCII); + } - Pattern pattern = Pattern.compile("(-?\\d+\\.\\d+), (-?\\d+\\.\\d+)"); - Matcher matcher = pattern.matcher(sentence); - if (!matcher.find()) { - return null; - } - position.setLatitude(Double.parseDouble(matcher.group(1))); - position.setLongitude(Double.parseDouble(matcher.group(2))); - - pattern = Pattern.compile("([NSWE]{1,2}) with speed (\\d+) km/h"); - matcher = pattern.matcher(sentence); - if (matcher.find()) { - for (int i = 0; i < DIRECTIONS.length; i++) { - if (matcher.group(1).equals(DIRECTIONS[i])) { - position.setCourse(i * 45.0); - break; - } - } - position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(matcher.group(2)))); - } + private Position decode04(Channel channel, SocketAddress remoteAddress, ByteBuf buf) { - pattern = Pattern.compile("(\\d{1,2}:\\d{2}(:\\d{2})? \\w{3} \\d{1,2})"); - matcher = pattern.matcher(sentence); - if (!matcher.find()) { - return null; - } - DateFormat dateFormat = new SimpleDateFormat( - matcher.group(2) != null ? "HH:mm:ss MMM d yyyy" : "HH:mm MMM d yyyy", Locale.ENGLISH); - position.setTime(DateUtil.correctYear( - dateFormat.parse(matcher.group(1) + " " + Calendar.getInstance().get(Calendar.YEAR)))); - - if (sentence.contains("Ignition on detected")) { - position.set(Position.KEY_IGNITION, true); - } else if (sentence.contains("Ignition off detected")) { - position.set(Position.KEY_IGNITION, false); + buf.readUnsignedShortLE(); // length + buf.readUnsignedShortLE(); // checksum + int index = buf.readUnsignedShortLE(); + long id1 = buf.readUnsignedIntLE(); + long id2 = buf.readUnsignedIntLE(); + long time = buf.readUnsignedIntLE(); + + String id = String.format("%08d%07d", id1, id2); + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id); + if (deviceSession == null) { + return null; + } + + if (channel != null) { + ByteBuf response = Unpooled.buffer(); + response.writeByte(0x04); // protocol + response.writeShortLE(24); // length + response.writeShortLE(0); // checksum + response.writeShortLE(index); + response.writeIntLE((int) id1); + response.writeIntLE((int) id2); + response.writeIntLE((int) time); + + response.writeByte(0xff); // acknowledgement + response.writeShortLE(index); + response.writeShortLE(0); // success + + response.setShortLE(3, Checksum.crc16(Checksum.CRC16_CCITT_FALSE, response.nioBuffer())); + + channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress())); + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + position.set(Position.KEY_INDEX, index); + + position.setDeviceTime(new Date(time * 1000)); + + while (buf.isReadable()) { + int type = buf.readUnsignedByte(); + switch (type) { + case 0: + position.set(Position.KEY_EVENT, buf.readUnsignedShortLE()); + buf.readUnsignedIntLE(); // event data + + int status = buf.readUnsignedShortLE(); + position.set(Position.KEY_IGNITION, BitUtil.check(status, 5)); + position.set(Position.KEY_STATUS, status); + + position.setValid(true); + position.setLatitude(buf.readIntLE() * 0.00001); + position.setLongitude(buf.readIntLE() * 0.00001); + position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShortLE())); + position.setCourse(buf.readUnsignedShortLE()); + + position.set(Position.KEY_RSSI, buf.readUnsignedByte()); + position.set(Position.KEY_GPS, buf.readUnsignedByte()); + position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte()); + position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedShortLE()); + position.set("maxAcceleration", buf.readUnsignedShortLE() * 0.001); + position.set("maxDeceleration", buf.readUnsignedShortLE() * 0.001); + buf.readUnsignedShortLE(); // bearing to landmark + buf.readUnsignedIntLE(); // distance to landmark + + position.setFixTime(new Date(buf.readUnsignedIntLE() * 1000)); + + buf.readUnsignedByte(); // reserved + break; + case 1: + buf.skipBytes(buf.readUnsignedShortLE() - 3); // landmark + break; + case 4: + buf.skipBytes(53); // trip + break; + case 20: + buf.skipBytes(32); // extended + break; + case 22: + buf.readUnsignedByte(); // zone flag + buf.skipBytes(buf.readUnsignedShortLE()); // zone name + break; + case 30: + buf.skipBytes(79); // system status + break; + case 40: + buf.skipBytes(40); // analog + break; + case 50: + buf.skipBytes(buf.readUnsignedShortLE() - 3); // console + break; + case 255: + buf.skipBytes(4); // acknowledgement + break; + default: + throw new IllegalArgumentException(String.format("Unknown type %d", type)); } + } - return position; + return position.getValid() ? position : null; + + } + private Position decode80(Channel channel, SocketAddress remoteAddress, ByteBuf buf) throws ParseException { + + buf.readUnsignedByte(); // version id + int index = buf.readUnsignedShort(); + buf.readUnsignedShort(); // type + + buf.readUnsignedShort(); // length + buf.readUnsignedShort(); // mask + buf.readUnsignedShort(); // checksum + long id = buf.readUnsignedInt(); + buf.readUnsignedInt(); // time + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(id)); + if (deviceSession == null) { + return null; } - return null; + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + position.set(Position.KEY_INDEX, index); + + if (channel != null) { + channel.writeAndFlush(new NetworkMessage( + Unpooled.copiedBuffer("gprs,ack," + index, StandardCharsets.US_ASCII), remoteAddress)); + } + + String sentence = buf.toString(StandardCharsets.US_ASCII); + + Pattern pattern = Pattern.compile("(-?\\d+\\.\\d+), (-?\\d+\\.\\d+)"); + Matcher matcher = pattern.matcher(sentence); + if (!matcher.find()) { + return null; + } + position.setLatitude(Double.parseDouble(matcher.group(1))); + position.setLongitude(Double.parseDouble(matcher.group(2))); + position.setValid(true); + + pattern = Pattern.compile("([NSWE]{1,2}) with speed (\\d+) km/h"); + matcher = pattern.matcher(sentence); + if (matcher.find()) { + for (int i = 0; i < DIRECTIONS.length; i++) { + if (matcher.group(1).equals(DIRECTIONS[i])) { + position.setCourse(i * 45.0); + break; + } + } + position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(matcher.group(2)))); + } + + pattern = Pattern.compile("(\\d{1,2}:\\d{2}(:\\d{2})? \\w{3} \\d{1,2})"); + matcher = pattern.matcher(sentence); + if (!matcher.find()) { + return null; + } + DateFormat dateFormat = new SimpleDateFormat( + matcher.group(2) != null ? "HH:mm:ss MMM d yyyy" : "HH:mm MMM d yyyy", Locale.ENGLISH); + position.setTime(DateUtil.correctYear( + dateFormat.parse(matcher.group(1) + " " + Calendar.getInstance().get(Calendar.YEAR)))); + + if (sentence.contains("Ignition on detected")) { + position.set(Position.KEY_IGNITION, true); + } else if (sentence.contains("Ignition off detected")) { + position.set(Position.KEY_IGNITION, false); + } + + return position; + } } diff --git a/src/main/java/org/traccar/protocol/TranSyncProtocol.java b/src/main/java/org/traccar/protocol/TranSyncProtocol.java new file mode 100644 index 000000000..fb37a1ab4 --- /dev/null +++ b/src/main/java/org/traccar/protocol/TranSyncProtocol.java @@ -0,0 +1,39 @@ +/* + * 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.protocol; + +import io.netty.handler.codec.LengthFieldBasedFrameDecoder; +import org.traccar.BaseProtocol; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; + +public class TranSyncProtocol extends BaseProtocol { + + @Inject + public TranSyncProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(new LengthFieldBasedFrameDecoder(256, 2, 1, 2, 0)); + pipeline.addLast(new TranSyncProtocolDecoder(TranSyncProtocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/TranSyncProtocolDecoder.java b/src/main/java/org/traccar/protocol/TranSyncProtocolDecoder.java new file mode 100644 index 000000000..816b5d2cf --- /dev/null +++ b/src/main/java/org/traccar/protocol/TranSyncProtocolDecoder.java @@ -0,0 +1,166 @@ +/* + * 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.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.Protocol; +import org.traccar.helper.BitUtil; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.UnitsConverter; +import org.traccar.model.CellTower; +import org.traccar.model.Network; +import org.traccar.model.Position; +import org.traccar.session.DeviceSession; + +import java.net.SocketAddress; + +public class TranSyncProtocolDecoder extends BaseProtocolDecoder { + + public TranSyncProtocolDecoder(Protocol protocol) { + super(protocol); + } + + private String decodeAlarm(int value) { + switch (value) { + case 4: + return Position.ALARM_LOW_BATTERY; + case 6: + return Position.ALARM_POWER_RESTORED; + case 10: + return Position.ALARM_SOS; + case 13: + return Position.ALARM_BRAKING; + case 14: + return Position.ALARM_ACCELERATION; + case 17: + return Position.ALARM_OVERSPEED; + case 23: + return Position.ALARM_ACCIDENT; + default: + return null; + } + } + + @Override + protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + ByteBuf buf = (ByteBuf) msg; + + buf.readUnsignedShort(); // header + buf.readByte(); // length + + int lac = buf.readUnsignedShort(); + + String deviceId = ByteBufUtil.hexDump(buf.readSlice(8)); + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, deviceId); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + buf.readUnsignedShort(); // index + buf.readUnsignedByte(); // type + + position.setTime(new DateBuilder() + .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()) + .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()) + .getDate()); + + double latitude = buf.readUnsignedInt() / 1800000.0; + double longitude = buf.readUnsignedInt() / 1800000.0; + + position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte())); + position.setCourse(buf.readUnsignedShort()); + + int mnc = buf.readUnsignedByte(); + int cid = buf.readUnsignedShort(); + int status0 = buf.readUnsignedByte(); + + position.setValid(BitUtil.check(status0, 0)); + position.setLatitude(BitUtil.check(status0, 1) ? latitude : -latitude); + position.setLongitude(BitUtil.check(status0, 2) ? longitude : -longitude); + + position.set(Position.PREFIX_OUT + 1, BitUtil.check(status0, 7)); + position.set(Position.PREFIX_OUT + 2, BitUtil.check(status0, 6)); + position.set(Position.PREFIX_IN + 3, BitUtil.check(status0, 5)); + if (BitUtil.check(status0, 4)) { + position.set(Position.KEY_ALARM, Position.ALARM_POWER_OFF); + } + position.set(Position.KEY_IGNITION, BitUtil.check(status0, 3)); + + buf.readUnsignedByte(); // reserved + + int event = buf.readUnsignedByte(); + position.set(Position.KEY_ALARM, decodeAlarm(event)); + position.set(Position.KEY_EVENT, event); + + int status3 = buf.readUnsignedByte(); + if (BitUtil.check(status3, 7)) { + position.set(Position.KEY_ARCHIVE, true); + } + if (BitUtil.check(status3, 5)) { + position.set(Position.KEY_ALARM, Position.ALARM_GPS_ANTENNA_CUT); + } + + int rssi = buf.readUnsignedByte(); + CellTower cellTower = CellTower.fromLacCid(getConfig(), lac, cid); + cellTower.setMobileNetworkCode(mnc); + cellTower.setSignalStrength(rssi); + position.setNetwork(new Network(cellTower)); + + position.set(Position.KEY_BATTERY, (double) (buf.readUnsignedByte() / 10)); + position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); + position.set(Position.KEY_HDOP, buf.readUnsignedByte()); + position.set(Position.PREFIX_ADC + 1, (short) buf.readUnsignedShort()); + + if (buf.readableBytes() > 5) { + buf.readUnsignedByte(); // odometer id + int length = buf.readUnsignedByte(); + if (length > 0) { + position.set(Position.KEY_ODOMETER, buf.readBytes(length).readInt()); + } + } + if (buf.readableBytes() > 5) { + buf.readUnsignedByte(); // rfid id + int length = buf.readUnsignedByte(); + if (length > 0) { + position.set(Position.KEY_DRIVER_UNIQUE_ID, ByteBufUtil.hexDump(buf.readSlice(length))); + } + } + if (buf.readableBytes() > 5) { + buf.readUnsignedByte(); // adc2 id + int length = buf.readUnsignedByte(); + if (length > 0) { + position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShort()); + } + } + if (buf.readableBytes() > 5) { + buf.readUnsignedByte(); // adc3 id + int length = buf.readUnsignedByte(); + if (length > 0 && length <= buf.readableBytes() - 2) { + position.set(Position.PREFIX_ADC + 3, buf.readUnsignedShort()); + } + } + + return position; + } + +} diff --git a/src/main/java/org/traccar/protocol/TrvProtocol.java b/src/main/java/org/traccar/protocol/TrvProtocol.java index 99a164cf1..7bdf3d2d0 100644 --- a/src/main/java/org/traccar/protocol/TrvProtocol.java +++ b/src/main/java/org/traccar/protocol/TrvProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class TrvProtocol extends BaseProtocol { - public TrvProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public TrvProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '#')); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java b/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java index e62cdf404..9df29ae1b 100644 --- a/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; diff --git a/src/main/java/org/traccar/protocol/Tt8850Protocol.java b/src/main/java/org/traccar/protocol/Tt8850Protocol.java index 66a13da9e..8e2800d90 100644 --- a/src/main/java/org/traccar/protocol/Tt8850Protocol.java +++ b/src/main/java/org/traccar/protocol/Tt8850Protocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class Tt8850Protocol extends BaseProtocol { - public Tt8850Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public Tt8850Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "$")); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/Tt8850ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tt8850ProtocolDecoder.java index 1010528c4..cbc983000 100644 --- a/src/main/java/org/traccar/protocol/Tt8850ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Tt8850ProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/TytanProtocol.java b/src/main/java/org/traccar/protocol/TytanProtocol.java index 32e9acae1..4fd3c807f 100644 --- a/src/main/java/org/traccar/protocol/TytanProtocol.java +++ b/src/main/java/org/traccar/protocol/TytanProtocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class TytanProtocol extends BaseProtocol { - public TytanProtocol() { - addServer(new TrackerServer(true, getName()) { + @Inject + public TytanProtocol(Config config) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new TytanProtocolDecoder(TytanProtocol.this)); } }); diff --git a/src/main/java/org/traccar/protocol/TytanProtocolDecoder.java b/src/main/java/org/traccar/protocol/TytanProtocolDecoder.java index 93d3a63d2..6169e0545 100644 --- a/src/main/java/org/traccar/protocol/TytanProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TytanProtocolDecoder.java @@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/TzoneProtocol.java b/src/main/java/org/traccar/protocol/TzoneProtocol.java index 6e855d138..2df721049 100644 --- a/src/main/java/org/traccar/protocol/TzoneProtocol.java +++ b/src/main/java/org/traccar/protocol/TzoneProtocol.java @@ -15,18 +15,21 @@ */ package org.traccar.protocol; +import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; -import io.netty.handler.codec.LengthFieldBasedFrameDecoder; +import jakarta.inject.Inject; public class TzoneProtocol extends BaseProtocol { - public TzoneProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public TzoneProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(256, 2, 2, 2, 0)); pipeline.addLast(new TzoneProtocolDecoder(TzoneProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java b/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java index 819c42471..f0b1e709d 100644 --- a/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java @@ -20,8 +20,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.config.Keys; @@ -205,30 +204,39 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder { } - private void decodeTags(Position position, ByteBuf buf) { + private void decodeTags(Position position, ByteBuf buf, int hardware) { int blockLength = buf.readUnsignedShort(); int blockEnd = buf.readerIndex() + blockLength; if (blockLength > 0) { - buf.readUnsignedByte(); // tag type + int type = buf.readUnsignedByte(); - int count = buf.readUnsignedByte(); - int tagLength = buf.readUnsignedByte(); + if (hardware != 0x153 || type >= 2) { - for (int i = 1; i <= count; i++) { - int tagEnd = buf.readerIndex() + tagLength; + int count = buf.readUnsignedByte(); + int tagLength = buf.readUnsignedByte(); + + for (int i = 1; i <= count; i++) { + int tagEnd = buf.readerIndex() + tagLength; + + buf.readUnsignedByte(); // status + buf.readUnsignedShortLE(); // battery voltage - buf.readUnsignedByte(); // status - buf.readUnsignedShortLE(); // battery voltage + position.set(Position.PREFIX_TEMP + i, (buf.readShortLE() & 0x3fff) * 0.1); + + buf.readUnsignedByte(); // humidity + buf.readUnsignedByte(); // rssi + + buf.readerIndex(tagEnd); + } - position.set(Position.PREFIX_TEMP + i, (buf.readShortLE() & 0x3fff) * 0.1); + } else if (type == 1) { - buf.readUnsignedByte(); // humidity - buf.readUnsignedByte(); // rssi + position.set(Position.KEY_CARD, buf.readCharSequence( + blockEnd - buf.readerIndex(), StandardCharsets.UTF_8).toString()); - buf.readerIndex(tagEnd); } } @@ -284,7 +292,7 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder { if (hardware == 0x10A || hardware == 0x10B || hardware == 0x406) { position.setNetwork(new Network( - CellTower.fromLacCid(buf.readUnsignedShort(), buf.readUnsignedShort()))); + CellTower.fromLacCid(getConfig(), buf.readUnsignedShort(), buf.readUnsignedShort()))); } else if (hardware == 0x407) { @@ -365,13 +373,13 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder { } - if (hardware == 0x406) { + if (hardware == 0x153 || hardware == 0x406) { - decodeTags(position, buf); + decodeTags(position, buf, hardware); } - if (Context.getConfig().getBoolean(Keys.PROTOCOL_ACK.withPrefix(getProtocolName()))) { + if (getConfig().getBoolean(Keys.PROTOCOL_ACK.withPrefix(getProtocolName()))) { sendResponse(channel, remoteAddress, buf.getUnsignedShort(buf.writerIndex() - 6)); } diff --git a/src/main/java/org/traccar/protocol/UlbotechProtocol.java b/src/main/java/org/traccar/protocol/UlbotechProtocol.java index dfe5235f0..f8c4f1960 100644 --- a/src/main/java/org/traccar/protocol/UlbotechProtocol.java +++ b/src/main/java/org/traccar/protocol/UlbotechProtocol.java @@ -18,16 +18,20 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class UlbotechProtocol extends BaseProtocol { - public UlbotechProtocol() { + @Inject + public UlbotechProtocol(Config config) { setSupportedDataCommands( Command.TYPE_CUSTOM); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new UlbotechFrameDecoder()); pipeline.addLast(new UlbotechProtocolEncoder(UlbotechProtocol.this)); pipeline.addLast(new UlbotechProtocolDecoder(UlbotechProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/UlbotechProtocolDecoder.java b/src/main/java/org/traccar/protocol/UlbotechProtocolDecoder.java index 7fec0bf8b..c9b35158e 100644 --- a/src/main/java/org/traccar/protocol/UlbotechProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/UlbotechProtocolDecoder.java @@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; @@ -37,6 +37,7 @@ import org.traccar.model.Position; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; import java.util.Date; +import java.util.TimeZone; import java.util.regex.Pattern; public class UlbotechProtocolDecoder extends BaseProtocolDecoder { @@ -214,16 +215,17 @@ public class UlbotechProtocolDecoder extends BaseProtocolDecoder { return null; } - if (deviceSession.getTimeZone() == null) { - deviceSession.setTimeZone(getTimeZone(deviceSession.getDeviceId())); + if (!deviceSession.contains(DeviceSession.KEY_TIMEZONE)) { + deviceSession.set(DeviceSession.KEY_TIMEZONE, getTimeZone(deviceSession.getDeviceId())); } Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); + TimeZone timeZone = deviceSession.get(DeviceSession.KEY_TIMEZONE); long seconds = buf.readUnsignedInt() & 0x7fffffffL; seconds += 946684800L; // 2000-01-01 00:00 - seconds -= deviceSession.getTimeZone().getRawOffset() / 1000; + seconds -= timeZone.getRawOffset() / 1000; Date time = new Date(seconds * 1000); boolean hasLocation = false; diff --git a/src/main/java/org/traccar/protocol/UproProtocol.java b/src/main/java/org/traccar/protocol/UproProtocol.java index 4e60ffeb6..cbec9777d 100644 --- a/src/main/java/org/traccar/protocol/UproProtocol.java +++ b/src/main/java/org/traccar/protocol/UproProtocol.java @@ -20,13 +20,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class UproProtocol extends BaseProtocol { - public UproProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public UproProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '#')); pipeline.addLast(new StringEncoder()); pipeline.addLast(new UproProtocolDecoder(UproProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/UproProtocolDecoder.java b/src/main/java/org/traccar/protocol/UproProtocolDecoder.java index 9f236a7e5..ed714e464 100644 --- a/src/main/java/org/traccar/protocol/UproProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/UproProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; @@ -67,12 +67,11 @@ public class UproProtocolDecoder extends BaseProtocolDecoder { DateBuilder dateBuilder = new DateBuilder() .setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0)); - position.setValid(true); position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_MIN_MIN)); position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_MIN_MIN)); int flags = parser.nextInt(0); - position.setValid(BitUtil.check(flags, 0)); + position.setValid(!BitUtil.check(flags, 0)); if (!BitUtil.check(flags, 1)) { position.setLatitude(-position.getLatitude()); } diff --git a/src/main/java/org/traccar/protocol/UuxProtocol.java b/src/main/java/org/traccar/protocol/UuxProtocol.java index 41b59d829..63727cb94 100644 --- a/src/main/java/org/traccar/protocol/UuxProtocol.java +++ b/src/main/java/org/traccar/protocol/UuxProtocol.java @@ -19,13 +19,17 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class UuxProtocol extends BaseProtocol { - public UuxProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public UuxProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 3, 1)); pipeline.addLast(new UuxProtocolDecoder(UuxProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/UuxProtocolDecoder.java b/src/main/java/org/traccar/protocol/UuxProtocolDecoder.java index 1081fa811..b9065e7f3 100644 --- a/src/main/java/org/traccar/protocol/UuxProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/UuxProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; diff --git a/src/main/java/org/traccar/protocol/V680Protocol.java b/src/main/java/org/traccar/protocol/V680Protocol.java index dc0922cd4..587a0c8f7 100644 --- a/src/main/java/org/traccar/protocol/V680Protocol.java +++ b/src/main/java/org/traccar/protocol/V680Protocol.java @@ -21,22 +21,26 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class V680Protocol extends BaseProtocol { - public V680Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public V680Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "##")); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new V680ProtocolDecoder(V680Protocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new V680ProtocolDecoder(V680Protocol.this)); diff --git a/src/main/java/org/traccar/protocol/V680ProtocolDecoder.java b/src/main/java/org/traccar/protocol/V680ProtocolDecoder.java index 40267022b..237aea39b 100644 --- a/src/main/java/org/traccar/protocol/V680ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/V680ProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/VisiontekProtocol.java b/src/main/java/org/traccar/protocol/VisiontekProtocol.java index 2c6af45a8..83bcd37ff 100644 --- a/src/main/java/org/traccar/protocol/VisiontekProtocol.java +++ b/src/main/java/org/traccar/protocol/VisiontekProtocol.java @@ -21,13 +21,17 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class VisiontekProtocol extends BaseProtocol { - public VisiontekProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public VisiontekProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '#')); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/VisiontekProtocolDecoder.java b/src/main/java/org/traccar/protocol/VisiontekProtocolDecoder.java index c4787bda2..9ab871bfe 100644 --- a/src/main/java/org/traccar/protocol/VisiontekProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/VisiontekProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/VltProtocol.java b/src/main/java/org/traccar/protocol/VltProtocol.java new file mode 100644 index 000000000..005cd8ffb --- /dev/null +++ b/src/main/java/org/traccar/protocol/VltProtocol.java @@ -0,0 +1,43 @@ +/* + * 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.protocol; + +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpRequestDecoder; +import io.netty.handler.codec.http.HttpResponseEncoder; +import org.traccar.BaseProtocol; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; + +public class VltProtocol extends BaseProtocol { + + @Inject + public VltProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(new HttpResponseEncoder()); + pipeline.addLast(new HttpRequestDecoder()); + pipeline.addLast(new HttpObjectAggregator(65535)); + pipeline.addLast(new VltProtocolDecoder(VltProtocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/VltProtocolDecoder.java b/src/main/java/org/traccar/protocol/VltProtocolDecoder.java new file mode 100644 index 000000000..01c0563f5 --- /dev/null +++ b/src/main/java/org/traccar/protocol/VltProtocolDecoder.java @@ -0,0 +1,139 @@ +/* + * 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.protocol; + +import io.netty.channel.Channel; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.QueryStringDecoder; +import org.traccar.BaseHttpProtocolDecoder; +import org.traccar.Protocol; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; +import org.traccar.model.CellTower; +import org.traccar.model.Network; +import org.traccar.model.Position; +import org.traccar.session.DeviceSession; + +import java.net.SocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.LinkedList; +import java.util.List; +import java.util.regex.Pattern; + +public class VltProtocolDecoder extends BaseHttpProtocolDecoder { + + public VltProtocolDecoder(Protocol protocol) { + super(protocol); + } + + private static final Pattern PATTERN = new PatternBuilder() + .number("(dd)") // alert id + .expression("([HL])") // history + .number("([01])") // validity + .number("(dd)(dd)(dd)") // date (ddmmyy) + .number("(dd)(dd)(dd)") // time (hhmmss) + .number("(d{3}.d{6})([NS])") // latitude + .number("(d{3}.d{6})([EW])") // longitude + .number("(d{3})") // mcc + .expression("(x*[0-9]+)") // mnc + .number("(x{4})") // lac + .number("(d{9})") // cid + .number("(d{3}.d{2})") // speed + .number("(d{3}.d{2})") // course + .number("(d{2})") // satellites + .number("(d{2})") // hdop + .number("(d{2})") // rssi + .number("([01])") // ignition + .number("([01])") // charging + .expression("([HMS])") // vehicle mode + .compile(); + + private Position decodePosition(DeviceSession deviceSession, String sentence) { + + Parser parser = new Parser(PATTERN, sentence); + if (!parser.matches()) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.set(Position.KEY_EVENT, parser.nextInt()); + position.set(Position.KEY_ARCHIVE, parser.next().equals("H") ? true : null); + + position.setValid(parser.nextInt() > 0); + position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS)); + position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM)); + position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM)); + + int mcc = parser.nextInt(); + int mnc = Integer.parseInt(parser.next().replaceAll("x", "")); + int lac = parser.nextHexInt(); + int cid = parser.nextInt(); + + position.setSpeed(parser.nextDouble()); + position.setCourse(parser.nextDouble()); + + position.set(Position.KEY_SATELLITES, parser.nextInt()); + position.set(Position.KEY_HDOP, parser.nextInt()); + + position.setNetwork(new Network(CellTower.from(mcc, mnc, lac, cid, parser.nextInt()))); + + position.set(Position.KEY_IGNITION, parser.nextInt() > 0); + position.set(Position.KEY_CHARGE, parser.nextInt() > 0); + position.set(Position.KEY_MOTION, parser.next().equals("M")); + + return position; + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + FullHttpRequest request = (FullHttpRequest) msg; + QueryStringDecoder decoder = new QueryStringDecoder( + request.content().toString(StandardCharsets.US_ASCII), false); + String sentence = decoder.parameters().get("vltdata").iterator().next(); + + int index = 0; + String type = sentence.substring(index, index += 3); + String imei = sentence.substring(index, index += 15); + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei); + if (deviceSession == null) { + sendResponse(channel, HttpResponseStatus.BAD_REQUEST); + return null; + } + + sendResponse(channel, HttpResponseStatus.OK); + + switch (type) { + case "NRM": + return decodePosition(deviceSession, sentence.substring(3 + 15)); + case "BTH": + List<Position> positions = new LinkedList<>(); + int count = Integer.parseInt(sentence.substring(index, index += 3)); + for (int i = 0; i < count; i++) { + positions.add(decodePosition(deviceSession, sentence.substring(index, index += 78))); + } + return positions; + default: + return null; + } + } + +} diff --git a/src/main/java/org/traccar/protocol/VnetProtocol.java b/src/main/java/org/traccar/protocol/VnetProtocol.java index 0fed30e13..6ccc54483 100644 --- a/src/main/java/org/traccar/protocol/VnetProtocol.java +++ b/src/main/java/org/traccar/protocol/VnetProtocol.java @@ -19,15 +19,19 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import java.nio.ByteOrder; +import jakarta.inject.Inject; + public class VnetProtocol extends BaseProtocol { - public VnetProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public VnetProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1500, 4, 2, 12, 0, true)); pipeline.addLast(new VnetProtocolDecoder(VnetProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/VnetProtocolDecoder.java b/src/main/java/org/traccar/protocol/VnetProtocolDecoder.java index 1ee00bd3f..048c89e1e 100644 --- a/src/main/java/org/traccar/protocol/VnetProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/VnetProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BcdUtil; diff --git a/src/main/java/org/traccar/protocol/Vt200Protocol.java b/src/main/java/org/traccar/protocol/Vt200Protocol.java index 2a9ef6ab5..97e64b74f 100644 --- a/src/main/java/org/traccar/protocol/Vt200Protocol.java +++ b/src/main/java/org/traccar/protocol/Vt200Protocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class Vt200Protocol extends BaseProtocol { - public Vt200Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public Vt200Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new Vt200FrameDecoder()); pipeline.addLast(new Vt200ProtocolDecoder(Vt200Protocol.this)); } diff --git a/src/main/java/org/traccar/protocol/Vt200ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Vt200ProtocolDecoder.java index 84ad09caa..1ad15f39c 100644 --- a/src/main/java/org/traccar/protocol/Vt200ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Vt200ProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.BcdUtil; import org.traccar.helper.BitUtil; @@ -123,7 +123,7 @@ public class Vt200ProtocolDecoder extends BaseProtocolDecoder { position.set("tripStart", decodeDate(buf).getTime()); position.set("tripEnd", decodeDate(buf).getTime()); - position.set("drivingTime", buf.readUnsignedShort()); + position.set(Position.KEY_DRIVING_TIME, buf.readUnsignedShort()); position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedInt()); position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedInt()); diff --git a/src/main/java/org/traccar/protocol/VtfmsProtocol.java b/src/main/java/org/traccar/protocol/VtfmsProtocol.java index 2826a86e6..91453c413 100644 --- a/src/main/java/org/traccar/protocol/VtfmsProtocol.java +++ b/src/main/java/org/traccar/protocol/VtfmsProtocol.java @@ -15,26 +15,29 @@ */ package org.traccar.protocol; +import io.netty.handler.codec.string.StringDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; -import io.netty.handler.codec.string.StringDecoder; +import jakarta.inject.Inject; public class VtfmsProtocol extends BaseProtocol { - public VtfmsProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public VtfmsProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new VtfmsFrameDecoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new VtfmsProtocolDecoder(VtfmsProtocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringDecoder()); pipeline.addLast(new VtfmsProtocolDecoder(VtfmsProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/VtfmsProtocolDecoder.java b/src/main/java/org/traccar/protocol/VtfmsProtocolDecoder.java index 17fac4311..bf0cdcb51 100644 --- a/src/main/java/org/traccar/protocol/VtfmsProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/VtfmsProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/WatchFrameDecoder.java b/src/main/java/org/traccar/protocol/WatchFrameDecoder.java index f99bd52e2..9dfae8726 100644 --- a/src/main/java/org/traccar/protocol/WatchFrameDecoder.java +++ b/src/main/java/org/traccar/protocol/WatchFrameDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 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. @@ -27,7 +27,26 @@ public class WatchFrameDecoder extends BaseFrameDecoder { protected Object decode( ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception { - int endIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) ']') + 1; + int brackets = 0; + int endIndex = -1; + for (int i = buf.readerIndex(); i < buf.writerIndex(); i++) { + byte b = buf.getByte(i); + switch (b) { + case '[': + brackets += 1; + break; + case ']': + brackets -= 1; + break; + default: + break; + } + if (brackets == 0 && i > buf.readerIndex()) { + endIndex = i + 1; + break; + } + } + if (endIndex > 0) { ByteBuf frame = Unpooled.buffer(); while (buf.readerIndex() < endIndex) { diff --git a/src/main/java/org/traccar/protocol/WatchProtocol.java b/src/main/java/org/traccar/protocol/WatchProtocol.java index 6dc3bf9fb..aee70b6ec 100644 --- a/src/main/java/org/traccar/protocol/WatchProtocol.java +++ b/src/main/java/org/traccar/protocol/WatchProtocol.java @@ -18,11 +18,15 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; +import jakarta.inject.Inject; + public class WatchProtocol extends BaseProtocol { - public WatchProtocol() { + @Inject + public WatchProtocol(Config config) { setSupportedDataCommands( Command.TYPE_CUSTOM, Command.TYPE_POSITION_SINGLE, @@ -40,9 +44,9 @@ public class WatchProtocol extends BaseProtocol { Command.TYPE_VOICE_MESSAGE, Command.TYPE_SET_TIMEZONE, Command.TYPE_SET_INDICATOR); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new WatchFrameDecoder()); pipeline.addLast(new WatchProtocolEncoder(WatchProtocol.this)); pipeline.addLast(new WatchProtocolDecoder(WatchProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java b/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java index 4ab7875b7..b586f4e92 100644 --- a/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 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. @@ -18,14 +18,13 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; +import org.traccar.helper.StringUtil; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; +import org.traccar.helper.BufferUtil; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; import org.traccar.helper.UnitsConverter; @@ -41,7 +40,7 @@ import java.util.regex.Pattern; public class WatchProtocolDecoder extends BaseProtocolDecoder { - private static final Logger LOGGER = LoggerFactory.getLogger(WatchProtocolDecoder.class); + private ByteBuf audio; public WatchProtocolDecoder(Protocol protocol) { super(protocol); @@ -52,7 +51,7 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder { .number("(dd)(dd)(dd),") // time (hhmmss) .expression("([AV]),") // validity .number(" *(-?d+.d+),") // latitude - .expression("([NS]),") + .expression("([NS])?,") .number(" *(-?d+.d+),") // longitude .expression("([EW])?,") .number("(d+.?d*),") // speed @@ -141,36 +140,45 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder { String[] values = parser.next().split(","); int index = 0; - Network network = new Network(); + if (values.length < 4 || !StringUtil.containsHex(values[index + 3])) { - int cellCount = Integer.parseInt(values[index++]); - index += 1; // timing advance - int mcc = !values[index].isEmpty() ? Integer.parseInt(values[index++]) : 0; - int mnc = !values[index].isEmpty() ? Integer.parseInt(values[index++]) : 0; + Network network = new Network(); - for (int i = 0; i < cellCount; i++) { - int lac = Integer.parseInt(values[index++]); - int cid = Integer.parseInt(values[index++]); - String rssi = values[index++]; - if (!rssi.isEmpty()) { - network.addCellTower(CellTower.from(mcc, mnc, lac, cid, Integer.parseInt(rssi))); - } else { - network.addCellTower(CellTower.from(mcc, mnc, lac, cid)); + int cellCount = Integer.parseInt(values[index++]); + if (cellCount > 0) { + index += 1; // timing advance + int mcc = !values[index].isEmpty() ? Integer.parseInt(values[index++]) : 0; + int mnc = !values[index].isEmpty() ? Integer.parseInt(values[index++]) : 0; + + for (int i = 0; i < cellCount; i++) { + int lac = Integer.parseInt(values[index], StringUtil.containsHex(values[index++]) ? 16 : 10); + int cid = Integer.parseInt(values[index], StringUtil.containsHex(values[index++]) ? 16 : 10); + String rssi = values[index++]; + if (!rssi.isEmpty()) { + network.addCellTower(CellTower.from(mcc, mnc, lac, cid, Integer.parseInt(rssi))); + } else { + network.addCellTower(CellTower.from(mcc, mnc, lac, cid)); + } + } } - } - if (index < values.length && !values[index].isEmpty()) { - int wifiCount = Integer.parseInt(values[index++]); + if (index < values.length && !values[index].isEmpty()) { + int wifiCount = Integer.parseInt(values[index++]); - for (int i = 0; i < wifiCount; i++) { - index += 1; // wifi name - network.addWifiAccessPoint(WifiAccessPoint.from( - values[index++], Integer.parseInt(values[index++]))); + for (int i = 0; i < wifiCount; i++) { + index += 1; // wifi name + String macAddress = values[index++]; + String rssi = values[index++]; + if (!macAddress.isEmpty() && !macAddress.equals("0") && !rssi.isEmpty()) { + network.addWifiAccessPoint(WifiAccessPoint.from(macAddress, Integer.parseInt(rssi))); + } + } + } + + if (network.getCellTowers() != null || network.getWifiAccessPoints() != null) { + position.setNetwork(network); } - } - if (network.getCellTowers() != null || network.getWifiAccessPoints() != null) { - position.setNetwork(network); } return position; @@ -260,6 +268,9 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder { Position position = decodePosition(deviceSession, buf.toString(StandardCharsets.US_ASCII)); if (type.startsWith("AL")) { + if (position != null && !position.hasAttribute(Position.KEY_ALARM)) { + position.set(Position.KEY_ALARM, Position.ALARM_GENERAL); + } sendResponse(channel, id, index, "AL"); } @@ -273,7 +284,9 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder { || type.equalsIgnoreCase("HEART") || type.equalsIgnoreCase("BLOOD") || type.equalsIgnoreCase("BPHRT") - || type.equalsIgnoreCase("btemp2")) { + || type.equalsIgnoreCase("TEMP") + || type.equalsIgnoreCase("btemp2") + || type.equalsIgnoreCase("oxygen")) { if (buf.isReadable()) { @@ -285,10 +298,14 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder { String[] values = buf.toString(StandardCharsets.US_ASCII).split(","); int valueIndex = 0; - if (type.equalsIgnoreCase("btemp2")) { + if (type.equalsIgnoreCase("TEMP")) { + position.set(Position.PREFIX_TEMP + 1, Double.parseDouble(values[valueIndex])); + } else if (type.equalsIgnoreCase("btemp2")) { if (Integer.parseInt(values[valueIndex++]) > 0) { position.set(Position.PREFIX_TEMP + 1, Double.parseDouble(values[valueIndex])); } + } else if (type.equalsIgnoreCase("oxygen")) { + position.set("bloodOxygen", Integer.parseInt(values[++valueIndex])); } else { if (type.equalsIgnoreCase("BPHRT") || type.equalsIgnoreCase("BLOOD")) { position.set("pressureHigh", values[valueIndex++]); @@ -312,17 +329,41 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder { int timeIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) ','); buf.readerIndex(timeIndex + 12 + 2); - position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(id, buf, "jpg")); + position.set(Position.KEY_IMAGE, writeMediaFile(id, buf, "jpg")); return position; + } else if (type.equals("JXTK")) { + + int dataIndex = BufferUtil.indexOf(buf, buf.readerIndex(), buf.writerIndex(), (byte) ',', 4) + 1; + String[] values = buf.readCharSequence( + dataIndex - buf.readerIndex(), StandardCharsets.US_ASCII).toString().split(","); + + int current = Integer.parseInt(values[2]); + int total = Integer.parseInt(values[3]); + + if (audio == null) { + audio = Unpooled.buffer(); + } + audio.writeBytes(buf); + + sendResponse(channel, id, index, "JXTKR,1"); + + if (current < total) { + return null; + } else { + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + getLastLocation(position, null); + position.set(Position.KEY_AUDIO, writeMediaFile(id, audio, "amr")); + audio.release(); + audio = null; + return position; + } + } else if (type.equals("TK")) { if (buf.readableBytes() == 1) { - byte result = buf.readByte(); - if (result != '1') { - LOGGER.warn(type + "," + result); - } return null; } @@ -331,7 +372,7 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder { getLastLocation(position, null); - position.set(Position.KEY_AUDIO, Context.getMediaManager().writeFile(id, buf, "amr")); + position.set(Position.KEY_AUDIO, writeMediaFile(id, buf, "amr")); return position; diff --git a/src/main/java/org/traccar/protocol/WatchProtocolEncoder.java b/src/main/java/org/traccar/protocol/WatchProtocolEncoder.java index f1904ea4d..14ebe2852 100644 --- a/src/main/java/org/traccar/protocol/WatchProtocolEncoder.java +++ b/src/main/java/org/traccar/protocol/WatchProtocolEncoder.java @@ -67,6 +67,9 @@ public class WatchProtocolEncoder extends StringProtocolEncoder implements Strin if (decoder != null) { hasIndex = decoder.getHasIndex(); manufacturer = decoder.getManufacturer(); + if (manufacturer.equals("3G")) { + manufacturer = "SG"; + } } } diff --git a/src/main/java/org/traccar/protocol/WialonProtocol.java b/src/main/java/org/traccar/protocol/WialonProtocol.java index cb6ea5319..84033132d 100644 --- a/src/main/java/org/traccar/protocol/WialonProtocol.java +++ b/src/main/java/org/traccar/protocol/WialonProtocol.java @@ -15,32 +15,34 @@ */ package org.traccar.protocol; +import io.netty.handler.codec.LineBasedFrameDecoder; +import io.netty.handler.codec.string.StringDecoder; +import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; -import org.traccar.Context; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.model.Command; -import io.netty.handler.codec.LineBasedFrameDecoder; -import io.netty.handler.codec.string.StringDecoder; -import io.netty.handler.codec.string.StringEncoder; - import java.nio.charset.StandardCharsets; +import jakarta.inject.Inject; + public class WialonProtocol extends BaseProtocol { - public WialonProtocol() { + @Inject + public WialonProtocol(Config config) { setSupportedDataCommands( Command.TYPE_REBOOT_DEVICE, Command.TYPE_SEND_USSD, Command.TYPE_IDENTIFICATION, Command.TYPE_OUTPUT_CONTROL); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(4 * 1024)); - boolean utf8 = Context.getConfig().getBoolean(Keys.PROTOCOL_UTF8.withPrefix(getName())); + boolean utf8 = config.getBoolean(Keys.PROTOCOL_UTF8.withPrefix(getName())); if (utf8) { pipeline.addLast(new StringEncoder(StandardCharsets.UTF_8)); pipeline.addLast(new StringDecoder(StandardCharsets.UTF_8)); @@ -52,11 +54,11 @@ public class WialonProtocol extends BaseProtocol { pipeline.addLast(new WialonProtocolDecoder(WialonProtocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(4 * 1024)); - boolean utf8 = Context.getConfig().getBoolean(Keys.PROTOCOL_UTF8.withPrefix(getName())); + boolean utf8 = config.getBoolean(Keys.PROTOCOL_UTF8.withPrefix(getName())); if (utf8) { pipeline.addLast(new StringEncoder(StandardCharsets.UTF_8)); pipeline.addLast(new StringDecoder(StandardCharsets.UTF_8)); diff --git a/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java b/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java index 19d9dd6c8..4d1b34dba 100644 --- a/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.Parser; @@ -39,7 +39,7 @@ public class WialonProtocolDecoder extends BaseProtocolDecoder { } private static final Pattern PATTERN_ANY = new PatternBuilder() - .expression("([^#]*)?") // imei + .expression("([^#]+)?") // imei .text("#") // start byte .expression("([^#]+)") // type .text("#") // separator @@ -63,8 +63,9 @@ public class WialonProtocolDecoder extends BaseProtocolDecoder { .number("(?:NA|(d+));") // outputs .expression("(?:NA|([^;]*));") // adc .expression("(?:NA|([^;]*));") // ibutton - .expression("(?:NA|(.*))") // params + .expression("(?:NA|([^;]*))") // params .groupEnd("?") + .any() .compile(); private void sendResponse(Channel channel, SocketAddress remoteAddress, String type, Integer number) { @@ -101,7 +102,7 @@ public class WialonProtocolDecoder extends BaseProtocolDecoder { position.setTime(new Date()); } - if (parser.hasNext(9)) { + if (parser.hasNextAny(9)) { position.setLatitude(parser.nextCoordinate()); position.setLongitude(parser.nextCoordinate()); position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0))); @@ -135,10 +136,22 @@ public class WialonProtocolDecoder extends BaseProtocolDecoder { for (String param : values) { Matcher paramParser = Pattern.compile("(.*):[1-3]:(.*)").matcher(param); if (paramParser.matches()) { + String key = paramParser.group(1).toLowerCase(); + String value = paramParser.group(2); try { - position.set(paramParser.group(1).toLowerCase(), Double.parseDouble(paramParser.group(2))); + if (key.equals("accuracy")) { + position.setAccuracy(Double.parseDouble(value)); + } else { + position.set(key, Double.parseDouble(value)); + } } catch (NumberFormatException e) { - position.set(paramParser.group(1).toLowerCase(), paramParser.group(2)); + if (value.equalsIgnoreCase("true")) { + position.set(key, true); + } else if (value.equalsIgnoreCase("false")) { + position.set(key, false); + } else { + position.set(key, value); + } } } } diff --git a/src/main/java/org/traccar/protocol/WliProtocol.java b/src/main/java/org/traccar/protocol/WliProtocol.java index c10ebf505..5b9ebb520 100644 --- a/src/main/java/org/traccar/protocol/WliProtocol.java +++ b/src/main/java/org/traccar/protocol/WliProtocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class WliProtocol extends BaseProtocol { - public WliProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public WliProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new WliFrameDecoder()); pipeline.addLast(new WliProtocolDecoder(WliProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/WliProtocolDecoder.java b/src/main/java/org/traccar/protocol/WliProtocolDecoder.java index 0e2a0a65e..ec1c4d17a 100644 --- a/src/main/java/org/traccar/protocol/WliProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/WliProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2020 - 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. @@ -18,10 +18,12 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; import org.traccar.helper.UnitsConverter; +import org.traccar.model.CellTower; +import org.traccar.model.Network; import org.traccar.model.Position; import java.net.SocketAddress; @@ -41,9 +43,9 @@ public class WliProtocolDecoder extends BaseProtocolDecoder { ByteBuf buf = (ByteBuf) msg; buf.readUnsignedByte(); // header - int type = buf.readUnsignedByte(); + int clazz = buf.readUnsignedByte(); - if (type == '1') { + if (clazz == '1') { DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); if (deviceSession == null) { @@ -53,11 +55,13 @@ public class WliProtocolDecoder extends BaseProtocolDecoder { Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); + CellTower cellTower = new CellTower(); + position.set(Position.KEY_INDEX, buf.readUnsignedShort()); buf.readUnsignedShort(); // length buf.readUnsignedShort(); // checksum - buf.readUnsignedByte(); // application message type + int type = buf.readUnsignedByte(); buf.readUnsignedByte(); // delimiter while (buf.readableBytes() > 1) { @@ -95,18 +99,56 @@ public class WliProtocolDecoder extends BaseProtocolDecoder { String value = buf.readCharSequence( endIndex - buf.readerIndex(), StandardCharsets.US_ASCII).toString(); - switch (fieldNumber) { - case 246: - String[] values = value.split(","); - position.set(Position.KEY_POWER, Integer.parseInt(values[2]) * 0.01); - position.set(Position.KEY_BATTERY, Integer.parseInt(values[3]) * 0.01); + int networkFieldsOffset; + switch (type) { + case 0xE4: + networkFieldsOffset = 10; + break; + case 0xCB: + networkFieldsOffset = 80; break; - case 255: - position.setDeviceTime(new Date(Long.parseLong(value) * 1000)); + case 0x1E: + networkFieldsOffset = 182; break; + case 0xC9: default: + networkFieldsOffset = 35; break; } + if (fieldNumber - networkFieldsOffset >= 0 && fieldNumber - networkFieldsOffset < 10) { + switch (fieldNumber - networkFieldsOffset) { + case 0: + cellTower.setMobileCountryCode(Integer.parseInt(value)); + break; + case 1: + cellTower.setMobileNetworkCode(Integer.parseInt(value)); + break; + case 2: + cellTower.setLocationAreaCode(Integer.parseInt(value)); + break; + case 3: + cellTower.setCellId(Long.parseLong(value)); + break; + case 4: + cellTower.setSignalStrength(Integer.parseInt(value)); + break; + default: + break; + } + } else { + switch (fieldNumber) { + case 246: + String[] values = value.split(","); + position.set(Position.KEY_POWER, Integer.parseInt(values[2]) * 0.01); + position.set(Position.KEY_BATTERY, Integer.parseInt(values[3]) * 0.01); + break; + case 255: + position.setDeviceTime(new Date(Long.parseLong(value) * 1000)); + break; + default: + break; + } + } } @@ -114,13 +156,21 @@ public class WliProtocolDecoder extends BaseProtocolDecoder { } + if (type == 0xE4) { + getLastLocation(position, position.getDeviceTime()); + } + + if (cellTower.getCellId() != null) { + position.setNetwork(new Network(cellTower)); + } + if (!position.getValid()) { getLastLocation(position, position.getDeviceTime()); } return position; - } else if (type == '2') { + } else if (clazz == '2') { String id = buf.toString(buf.readerIndex(), buf.readableBytes() - 1, StandardCharsets.US_ASCII); getDeviceSession(channel, remoteAddress, id.substring("wli:".length())); diff --git a/src/main/java/org/traccar/protocol/WondexProtocol.java b/src/main/java/org/traccar/protocol/WondexProtocol.java index 6401fde85..e27b8e2bb 100644 --- a/src/main/java/org/traccar/protocol/WondexProtocol.java +++ b/src/main/java/org/traccar/protocol/WondexProtocol.java @@ -15,16 +15,19 @@ */ package org.traccar.protocol; +import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; -import io.netty.handler.codec.string.StringEncoder; +import jakarta.inject.Inject; public class WondexProtocol extends BaseProtocol { - public WondexProtocol() { + @Inject + public WondexProtocol(Config config) { setSupportedDataCommands( Command.TYPE_GET_DEVICE_STATUS, Command.TYPE_GET_MODEM_STATUS, @@ -40,18 +43,18 @@ public class WondexProtocol extends BaseProtocol { Command.TYPE_POSITION_SINGLE, Command.TYPE_GET_VERSION, Command.TYPE_IDENTIFICATION); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new WondexFrameDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new WondexProtocolEncoder(WondexProtocol.this)); pipeline.addLast(new WondexProtocolDecoder(WondexProtocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringEncoder()); pipeline.addLast(new WondexProtocolEncoder(WondexProtocol.this)); pipeline.addLast(new WondexProtocolDecoder(WondexProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/WondexProtocolDecoder.java b/src/main/java/org/traccar/protocol/WondexProtocolDecoder.java index b85ae2656..46aa65a5d 100644 --- a/src/main/java/org/traccar/protocol/WondexProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/WondexProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 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. @@ -18,7 +18,7 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; @@ -74,6 +74,9 @@ public class WondexProtocolDecoder extends BaseProtocolDecoder { || buf.toString(StandardCharsets.US_ASCII).startsWith("$MSG:")) { DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); + if (deviceSession == null) { + return null; + } Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); @@ -89,12 +92,12 @@ public class WondexProtocolDecoder extends BaseProtocolDecoder { return null; } - Position position = new Position(getProtocolName()); - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); if (deviceSession == null) { return null; } + + Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); position.setTime(parser.nextDateTime()); diff --git a/src/main/java/org/traccar/protocol/WristbandProtocol.java b/src/main/java/org/traccar/protocol/WristbandProtocol.java index 1e5ef2c01..117daf8cf 100644 --- a/src/main/java/org/traccar/protocol/WristbandProtocol.java +++ b/src/main/java/org/traccar/protocol/WristbandProtocol.java @@ -19,13 +19,17 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class WristbandProtocol extends BaseProtocol { - public WristbandProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public WristbandProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 3, 2, 3, 0)); pipeline.addLast(new WristbandProtocolDecoder(WristbandProtocol.this)); } diff --git a/src/main/java/org/traccar/protocol/WristbandProtocolDecoder.java b/src/main/java/org/traccar/protocol/WristbandProtocolDecoder.java index 58b5784d0..323992ddd 100644 --- a/src/main/java/org/traccar/protocol/WristbandProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/WristbandProtocolDecoder.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/Xexun2FrameEncoder.java b/src/main/java/org/traccar/protocol/Xexun2FrameEncoder.java new file mode 100644 index 000000000..52d43c36c --- /dev/null +++ b/src/main/java/org/traccar/protocol/Xexun2FrameEncoder.java @@ -0,0 +1,48 @@ +/* + * Copyright 2022 Stefan Clark (stefan@stefanclark.co.uk) + * + * 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 Xexun2FrameEncoder extends MessageToByteEncoder<ByteBuf> { + + @Override + protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) { + out.writeBytes(msg.readBytes(2)); + + while (msg.readableBytes() > 2) { + int b = msg.readUnsignedByte(); + if (b == 0xfa && msg.isReadable() && msg.getUnsignedByte(msg.readerIndex()) == 0xaf) { + msg.readUnsignedByte(); + out.writeByte(0xfb); + out.writeByte(0xbf); + out.writeByte(0x01); + } else if (b == 0xfb && msg.isReadable() && msg.getUnsignedByte(msg.readerIndex()) == 0xbf) { + msg.readUnsignedByte(); + out.writeByte(0xfb); + out.writeByte(0xbf); + out.writeByte(0x02); + } else { + out.writeByte(b); + } + } + + out.writeBytes(msg.readBytes(2)); + + } +} diff --git a/src/main/java/org/traccar/protocol/Xexun2Protocol.java b/src/main/java/org/traccar/protocol/Xexun2Protocol.java index 265841c77..9dd517cfa 100644 --- a/src/main/java/org/traccar/protocol/Xexun2Protocol.java +++ b/src/main/java/org/traccar/protocol/Xexun2Protocol.java @@ -18,15 +18,27 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; +import org.traccar.model.Command; + +import jakarta.inject.Inject; public class Xexun2Protocol extends BaseProtocol { - public Xexun2Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public Xexun2Protocol(Config config) { + setSupportedDataCommands( + Command.TYPE_CUSTOM, + Command.TYPE_POSITION_PERIODIC, + Command.TYPE_POWER_OFF, + Command.TYPE_REBOOT_DEVICE); + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(new Xexun2FrameEncoder()); pipeline.addLast(new Xexun2FrameDecoder()); pipeline.addLast(new Xexun2ProtocolDecoder(Xexun2Protocol.this)); + pipeline.addLast(new Xexun2ProtocolEncoder(Xexun2Protocol.this)); } }); } diff --git a/src/main/java/org/traccar/protocol/Xexun2ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Xexun2ProtocolDecoder.java index 766a3f05b..0e3c44e12 100644 --- a/src/main/java/org/traccar/protocol/Xexun2ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Xexun2ProtocolDecoder.java @@ -20,10 +20,11 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; +import org.traccar.helper.Checksum; import org.traccar.helper.UnitsConverter; import org.traccar.model.CellTower; import org.traccar.model.Network; @@ -41,13 +42,14 @@ public class Xexun2ProtocolDecoder extends BaseProtocolDecoder { super(protocol); } + public static final int FLAG = 0xfaaf; + public static final int MSG_COMMAND = 0x07; public static final int MSG_POSITION = 0x14; private void sendResponse(Channel channel, int type, int index, ByteBuf imei) { if (channel != null) { ByteBuf response = Unpooled.buffer(); - response.writeByte(0xfa); - response.writeByte(0xaf); + response.writeShort(FLAG); response.writeShort(type); response.writeShort(index); @@ -56,8 +58,7 @@ public class Xexun2ProtocolDecoder extends BaseProtocolDecoder { response.writeShort(0xfffe); // checksum response.writeByte(1); // response - response.writeByte(0xfa); - response.writeByte(0xaf); + response.writeShort(FLAG); channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress())); } @@ -67,6 +68,9 @@ public class Xexun2ProtocolDecoder extends BaseProtocolDecoder { if (BitUtil.check(value, 0)) { return Position.ALARM_SOS; } + if (BitUtil.check(value, 1)) { + return Position.ALARM_REMOVING; + } if (BitUtil.check(value, 15)) { return Position.ALARM_FALL_DOWN; } @@ -96,10 +100,16 @@ public class Xexun2ProtocolDecoder extends BaseProtocolDecoder { return null; } - sendResponse(channel, type, index, imei); + int payloadSize = buf.readUnsignedShort() & 0x03ff; + int checksum = buf.readUnsignedShort(); + + if (checksum != Checksum.ip(buf.nioBuffer(buf.readerIndex(), payloadSize))) { + return null; + } - buf.readUnsignedShort(); // attributes - buf.readUnsignedShort(); // checksum + if (type != MSG_COMMAND) { + sendResponse(channel, type, index, imei); + } if (type == MSG_POSITION) { List<Integer> lengths = new ArrayList<>(); @@ -146,7 +156,7 @@ public class Xexun2ProtocolDecoder extends BaseProtocolDecoder { for (int j = 0; j < wifiCount; j++) { String mac = ByteBufUtil.hexDump(buf.readSlice(6)).replaceAll("(..)", "$1:"); network.addWifiAccessPoint(WifiAccessPoint.from( - mac.substring(0, mac.length() - 1), buf.readUnsignedByte())); + mac.substring(0, mac.length() - 1), buf.readByte())); } } if (BitUtil.check(positionMask, 2)) { @@ -154,7 +164,7 @@ public class Xexun2ProtocolDecoder extends BaseProtocolDecoder { for (int j = 0; j < cellCount; j++) { network.addCellTower(CellTower.from( buf.readUnsignedShort(), buf.readUnsignedShort(), - buf.readInt(), buf.readUnsignedInt(), buf.readUnsignedByte())); + buf.readInt(), buf.readUnsignedInt(), buf.readByte())); } } if (network.getWifiAccessPoints() != null || network.getCellTowers() != null) { @@ -173,7 +183,25 @@ public class Xexun2ProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); position.setLongitude(convertCoordinate(buf.readDouble())); position.setLatitude(convertCoordinate(buf.readDouble())); - + } + if (BitUtil.check(positionMask, 7)) { + int dataLength = buf.readUnsignedShort(); + if (dataLength > 0) { + int dataType = buf.readUnsignedByte(); + int dataEndIndex = buf.readerIndex() + buf.readUnsignedShort(); + if (dataType == 'G') { + position.setFixTime(position.getDeviceTime()); + position.setLongitude(convertCoordinate(buf.readDouble())); + position.setLatitude(convertCoordinate(buf.readDouble())); + position.setValid(buf.readUnsignedByte() > 0); + position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); + buf.readUnsignedByte(); // satellite signal-to-noise ratio + position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort() * 0.1)); + position.setCourse(buf.readUnsignedShort() * 0.1); + position.setAltitude(buf.readFloat()); + } + buf.readerIndex(dataEndIndex); + } } } if (BitUtil.check(mask, 3)) { diff --git a/src/main/java/org/traccar/protocol/Xexun2ProtocolEncoder.java b/src/main/java/org/traccar/protocol/Xexun2ProtocolEncoder.java new file mode 100644 index 000000000..8f3fa5672 --- /dev/null +++ b/src/main/java/org/traccar/protocol/Xexun2ProtocolEncoder.java @@ -0,0 +1,70 @@ +/* + * Copyright 2022 Stefan Clark (stefan@stefanclark.co.uk) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import org.traccar.BaseProtocolEncoder; +import org.traccar.helper.Checksum; +import org.traccar.helper.DataConverter; +import org.traccar.model.Command; +import org.traccar.Protocol; + +import java.nio.charset.StandardCharsets; + +public class Xexun2ProtocolEncoder extends BaseProtocolEncoder { + + public Xexun2ProtocolEncoder(Protocol protocol) { + super(protocol); + } + + private static ByteBuf encodeContent(String uniqueId, String content) { + ByteBuf buf = Unpooled.buffer(); + + ByteBuf message = Unpooled.copiedBuffer(content.getBytes(StandardCharsets.US_ASCII)); + + buf.writeShort(Xexun2ProtocolDecoder.FLAG); + buf.writeShort(Xexun2ProtocolDecoder.MSG_COMMAND); + buf.writeShort(1); // index + buf.writeBytes(DataConverter.parseHex(uniqueId + "0")); + buf.writeShort(message.readableBytes()); + buf.writeShort(Checksum.ip(message.nioBuffer())); + buf.writeBytes(message); + buf.writeShort(Xexun2ProtocolDecoder.FLAG); + + return buf; + } + + @Override + protected Object encodeCommand(Command command) { + String uniqueId = getUniqueId(command.getDeviceId()); + + switch (command.getType()) { + case Command.TYPE_CUSTOM: + return encodeContent(uniqueId, command.getString(Command.KEY_DATA)); + case Command.TYPE_POSITION_PERIODIC: + return encodeContent(uniqueId, + String.format("tracking_send=%1$d,%1$d", command.getInteger(Command.KEY_FREQUENCY))); + case Command.TYPE_POWER_OFF: + return encodeContent(uniqueId, "of=1"); + case Command.TYPE_REBOOT_DEVICE: + return encodeContent(uniqueId, "reset"); + default: + return null; + } + } + +} diff --git a/src/main/java/org/traccar/protocol/XexunProtocol.java b/src/main/java/org/traccar/protocol/XexunProtocol.java index b83c4e445..e76e47d19 100644 --- a/src/main/java/org/traccar/protocol/XexunProtocol.java +++ b/src/main/java/org/traccar/protocol/XexunProtocol.java @@ -15,27 +15,29 @@ */ package org.traccar.protocol; +import io.netty.handler.codec.LineBasedFrameDecoder; +import io.netty.handler.codec.string.StringDecoder; +import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; -import org.traccar.Context; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.model.Command; -import io.netty.handler.codec.LineBasedFrameDecoder; -import io.netty.handler.codec.string.StringDecoder; -import io.netty.handler.codec.string.StringEncoder; +import jakarta.inject.Inject; public class XexunProtocol extends BaseProtocol { - public XexunProtocol() { + @Inject + public XexunProtocol(Config config) { setSupportedDataCommands( Command.TYPE_ENGINE_STOP, Command.TYPE_ENGINE_RESUME); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { - boolean full = Context.getConfig().getBoolean(Keys.PROTOCOL_EXTENDED.withPrefix(getName())); + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + boolean full = config.getBoolean(Keys.PROTOCOL_EXTENDED.withPrefix(getName())); if (full) { pipeline.addLast(new LineBasedFrameDecoder(1024)); // tracker bug \n\r } else { diff --git a/src/main/java/org/traccar/protocol/XexunProtocolDecoder.java b/src/main/java/org/traccar/protocol/XexunProtocolDecoder.java index 73d386477..e41d467d5 100644 --- a/src/main/java/org/traccar/protocol/XexunProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/XexunProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/protocol/XirgoProtocol.java b/src/main/java/org/traccar/protocol/XirgoProtocol.java index 1be5b6c4b..7e14c6842 100644 --- a/src/main/java/org/traccar/protocol/XirgoProtocol.java +++ b/src/main/java/org/traccar/protocol/XirgoProtocol.java @@ -15,23 +15,26 @@ */ package org.traccar.protocol; +import io.netty.handler.codec.string.StringDecoder; +import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; -import io.netty.handler.codec.string.StringDecoder; -import io.netty.handler.codec.string.StringEncoder; +import jakarta.inject.Inject; public class XirgoProtocol extends BaseProtocol { - public XirgoProtocol() { + @Inject + public XirgoProtocol(Config config) { setSupportedDataCommands( Command.TYPE_OUTPUT_CONTROL); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "##")); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); @@ -39,9 +42,9 @@ public class XirgoProtocol extends BaseProtocol { pipeline.addLast(new XirgoProtocolDecoder(XirgoProtocol.this)); } }); - addServer(new TrackerServer(true, getName()) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new XirgoProtocolEncoder(XirgoProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/XirgoProtocolDecoder.java b/src/main/java/org/traccar/protocol/XirgoProtocolDecoder.java index 630fe5aed..220c28054 100644 --- a/src/main/java/org/traccar/protocol/XirgoProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/XirgoProtocolDecoder.java @@ -18,8 +18,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import io.netty.channel.socket.nio.NioDatagramChannel; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.config.Keys; @@ -40,7 +39,11 @@ public class XirgoProtocolDecoder extends BaseProtocolDecoder { public XirgoProtocolDecoder(Protocol protocol) { super(protocol); - form = Context.getConfig().getString(Keys.PROTOCOL_FORM.withPrefix(getProtocolName())); + } + + @Override + protected void init() { + form = getConfig().getString(Keys.PROTOCOL_FORM.withPrefix(getProtocolName())); } public void setForm(String form) { diff --git a/src/main/java/org/traccar/protocol/Xrb28Protocol.java b/src/main/java/org/traccar/protocol/Xrb28Protocol.java index 5d8af418b..135fb0928 100644 --- a/src/main/java/org/traccar/protocol/Xrb28Protocol.java +++ b/src/main/java/org/traccar/protocol/Xrb28Protocol.java @@ -21,22 +21,26 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; import org.traccar.model.Command; import java.nio.charset.StandardCharsets; +import jakarta.inject.Inject; + public class Xrb28Protocol extends BaseProtocol { - public Xrb28Protocol() { + @Inject + public Xrb28Protocol(Config config) { setSupportedDataCommands( Command.TYPE_CUSTOM, Command.TYPE_POSITION_SINGLE, Command.TYPE_POSITION_PERIODIC, Command.TYPE_ALARM_ARM, Command.TYPE_ALARM_DISARM); - addServer(new TrackerServer(false, getName()) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringEncoder(StandardCharsets.ISO_8859_1)); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java index 69e5b7372..88f2d07e5 100644 --- a/src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.DateBuilder; diff --git a/src/main/java/org/traccar/protocol/Xt013Protocol.java b/src/main/java/org/traccar/protocol/Xt013Protocol.java index ebb3c123f..25809463a 100644 --- a/src/main/java/org/traccar/protocol/Xt013Protocol.java +++ b/src/main/java/org/traccar/protocol/Xt013Protocol.java @@ -15,20 +15,23 @@ */ package org.traccar.protocol; +import io.netty.handler.codec.LineBasedFrameDecoder; +import io.netty.handler.codec.string.StringDecoder; +import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; -import io.netty.handler.codec.LineBasedFrameDecoder; -import io.netty.handler.codec.string.StringDecoder; -import io.netty.handler.codec.string.StringEncoder; +import jakarta.inject.Inject; public class Xt013Protocol extends BaseProtocol { - public Xt013Protocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public Xt013Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); diff --git a/src/main/java/org/traccar/protocol/Xt013ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Xt013ProtocolDecoder.java index f49fb9563..ab0b2cdaa 100644 --- a/src/main/java/org/traccar/protocol/Xt013ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Xt013ProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; diff --git a/src/main/java/org/traccar/protocol/Xt2400Protocol.java b/src/main/java/org/traccar/protocol/Xt2400Protocol.java index 9427876c8..1b7fc840b 100644 --- a/src/main/java/org/traccar/protocol/Xt2400Protocol.java +++ b/src/main/java/org/traccar/protocol/Xt2400Protocol.java @@ -18,13 +18,17 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class Xt2400Protocol extends BaseProtocol { - public Xt2400Protocol() { - addServer(new TrackerServer(true, getName()) { + @Inject + public Xt2400Protocol(Config config) { + addServer(new TrackerServer(config, getName(), true) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new Xt2400ProtocolDecoder(Xt2400Protocol.this)); } }); diff --git a/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java index 85e8e140f..edcb3f535 100644 --- a/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java @@ -18,8 +18,7 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.Context; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.config.Keys; import org.traccar.helper.DataConverter; @@ -38,8 +37,11 @@ public class Xt2400ProtocolDecoder extends BaseProtocolDecoder { public Xt2400ProtocolDecoder(Protocol protocol) { super(protocol); + } - String config = Context.getConfig().getString(Keys.PROTOCOL_CONFIG.withPrefix(getProtocolName())); + @Override + protected void init() { + String config = getConfig().getString(Keys.PROTOCOL_CONFIG.withPrefix(getProtocolName())); if (config != null) { setConfig(config); } diff --git a/src/main/java/org/traccar/protocol/YwtProtocol.java b/src/main/java/org/traccar/protocol/YwtProtocol.java index c525b75cf..27c71cfa8 100644 --- a/src/main/java/org/traccar/protocol/YwtProtocol.java +++ b/src/main/java/org/traccar/protocol/YwtProtocol.java @@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; public class YwtProtocol extends BaseProtocol { - public YwtProtocol() { - addServer(new TrackerServer(false, getName()) { + @Inject + public YwtProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { @Override - protected void addProtocolHandlers(PipelineBuilder pipeline) { + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); diff --git a/src/main/java/org/traccar/protocol/YwtProtocolDecoder.java b/src/main/java/org/traccar/protocol/YwtProtocolDecoder.java index bf5a23fa7..fd050bee9 100644 --- a/src/main/java/org/traccar/protocol/YwtProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/YwtProtocolDecoder.java @@ -17,7 +17,7 @@ package org.traccar.protocol; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.DeviceSession; +import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.Parser; diff --git a/src/main/java/org/traccar/reports/CombinedReportProvider.java b/src/main/java/org/traccar/reports/CombinedReportProvider.java new file mode 100644 index 000000000..bad3a61b3 --- /dev/null +++ b/src/main/java/org/traccar/reports/CombinedReportProvider.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.reports; + +import org.traccar.helper.model.DeviceUtil; +import org.traccar.helper.model.PositionUtil; +import org.traccar.model.Device; +import org.traccar.model.Event; +import org.traccar.reports.common.ReportUtils; +import org.traccar.reports.model.CombinedReportItem; +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.Order; +import org.traccar.storage.query.Request; + +import jakarta.inject.Inject; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.Set; +import java.util.stream.Collectors; + +public class CombinedReportProvider { + + private static final Set<String> EXCLUDE_TYPES = Set.of(Event.TYPE_DEVICE_MOVING); + + private final ReportUtils reportUtils; + private final Storage storage; + + @Inject + public CombinedReportProvider(ReportUtils reportUtils, Storage storage) { + this.reportUtils = reportUtils; + this.storage = storage; + } + + public Collection<CombinedReportItem> getObjects( + long userId, Collection<Long> deviceIds, Collection<Long> groupIds, + Date from, Date to) throws StorageException { + reportUtils.checkPeriodLimit(from, to); + + ArrayList<CombinedReportItem> result = new ArrayList<>(); + for (Device device: DeviceUtil.getAccessibleDevices(storage, userId, deviceIds, groupIds)) { + CombinedReportItem item = new CombinedReportItem(); + item.setDeviceId(device.getId()); + var positions = PositionUtil.getPositions(storage, device.getId(), from, to); + item.setRoute(positions.stream() + .map(p -> new double[] {p.getLongitude(), p.getLatitude()}) + .collect(Collectors.toList())); + var events = storage.getObjects(Event.class, new Request( + new Columns.All(), + new Condition.And( + new Condition.Equals("deviceId", device.getId()), + new Condition.Between("eventTime", "from", from, "to", to)), + new Order("eventTime"))); + item.setEvents(events.stream() + .filter(e -> e.getPositionId() > 0 && !EXCLUDE_TYPES.contains(e.getType())) + .collect(Collectors.toList())); + var eventPositions = events.stream() + .map(Event::getPositionId) + .collect(Collectors.toSet()); + item.setPositions(positions.stream() + .filter(p -> eventPositions.contains(p.getId())) + .collect(Collectors.toList())); + result.add(item); + } + return result; + } +} diff --git a/src/main/java/org/traccar/reports/CsvExportProvider.java b/src/main/java/org/traccar/reports/CsvExportProvider.java new file mode 100644 index 000000000..521dc120a --- /dev/null +++ b/src/main/java/org/traccar/reports/CsvExportProvider.java @@ -0,0 +1,76 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.reports; + +import org.traccar.helper.DateUtil; +import org.traccar.helper.model.PositionUtil; +import org.traccar.model.Position; +import org.traccar.storage.Storage; +import org.traccar.storage.StorageException; + +import jakarta.inject.Inject; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class CsvExportProvider { + + private final Storage storage; + + @Inject + public CsvExportProvider(Storage storage) { + this.storage = storage; + } + + public void generate( + OutputStream outputStream, long deviceId, Date from, Date to) throws StorageException { + + var positions = PositionUtil.getPositions(storage, deviceId, from, to); + + var attributes = positions.stream() + .flatMap((position -> position.getAttributes().keySet().stream())) + .collect(Collectors.toUnmodifiableSet()); + + var properties = new LinkedHashMap<String, Function<Position, Object>>(); + properties.put("id", Position::getId); + properties.put("deviceId", Position::getDeviceId); + properties.put("protocol", Position::getProtocol); + properties.put("serverTime", position -> DateUtil.formatDate(position.getServerTime())); + properties.put("deviceTime", position -> DateUtil.formatDate(position.getDeviceTime())); + properties.put("fixTime", position -> DateUtil.formatDate(position.getFixTime())); + properties.put("valid", Position::getValid); + properties.put("latitude", Position::getLatitude); + properties.put("longitude", Position::getLongitude); + properties.put("altitude", Position::getAltitude); + properties.put("speed", Position::getSpeed); + properties.put("course", Position::getCourse); + properties.put("address", Position::getAddress); + properties.put("accuracy", Position::getAccuracy); + attributes.forEach(key -> properties.put(key, position -> position.getAttributes().get(key))); + + try (PrintWriter writer = new PrintWriter(outputStream)) { + writer.println(String.join(",", properties.keySet())); + positions.forEach(position -> writer.println(properties.values().stream() + .map(f -> Objects.toString(f.apply(position), "")) + .collect(Collectors.joining(",")))); + } + } + +} diff --git a/src/main/java/org/traccar/reports/Events.java b/src/main/java/org/traccar/reports/Events.java deleted file mode 100644 index e4b905702..000000000 --- a/src/main/java/org/traccar/reports/Events.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org) - * Copyright 2016 - 2018 Andrey Kunitsyn (andrey@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.reports; - -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.Iterator; - -import org.apache.poi.ss.util.WorkbookUtil; -import org.traccar.Context; -import org.traccar.model.Device; -import org.traccar.model.Event; -import org.traccar.model.Geofence; -import org.traccar.model.Group; -import org.traccar.model.Maintenance; -import org.traccar.reports.model.DeviceReport; -import org.traccar.storage.StorageException; - -public final class Events { - - private Events() { - } - - public static Collection<Event> getObjects(long userId, Collection<Long> deviceIds, Collection<Long> groupIds, - Collection<String> types, Date from, Date to) throws StorageException { - ReportUtils.checkPeriodLimit(from, to); - ArrayList<Event> result = new ArrayList<>(); - for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) { - Context.getPermissionsManager().checkDevice(userId, deviceId); - Collection<Event> events = Context.getDataManager().getEvents(deviceId, from, to); - boolean all = types.isEmpty() || types.contains(Event.ALL_EVENTS); - for (Event event : events) { - if (all || types.contains(event.getType())) { - long geofenceId = event.getGeofenceId(); - long maintenanceId = event.getMaintenanceId(); - if ((geofenceId == 0 || Context.getGeofenceManager().checkItemPermission(userId, geofenceId)) - && (maintenanceId == 0 - || Context.getMaintenancesManager().checkItemPermission(userId, maintenanceId))) { - result.add(event); - } - } - } - } - return result; - } - - public static void getExcel(OutputStream outputStream, - long userId, Collection<Long> deviceIds, Collection<Long> groupIds, - Collection<String> types, Date from, Date to) throws StorageException, IOException { - ReportUtils.checkPeriodLimit(from, to); - ArrayList<DeviceReport> devicesEvents = new ArrayList<>(); - ArrayList<String> sheetNames = new ArrayList<>(); - HashMap<Long, String> geofenceNames = new HashMap<>(); - HashMap<Long, String> maintenanceNames = new HashMap<>(); - for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) { - Context.getPermissionsManager().checkDevice(userId, deviceId); - Collection<Event> events = Context.getDataManager().getEvents(deviceId, from, to); - boolean all = types.isEmpty() || types.contains(Event.ALL_EVENTS); - for (Iterator<Event> iterator = events.iterator(); iterator.hasNext();) { - Event event = iterator.next(); - if (all || types.contains(event.getType())) { - long geofenceId = event.getGeofenceId(); - long maintenanceId = event.getMaintenanceId(); - if (geofenceId != 0) { - if (Context.getGeofenceManager().checkItemPermission(userId, geofenceId)) { - Geofence geofence = Context.getGeofenceManager().getById(geofenceId); - if (geofence != null) { - geofenceNames.put(geofenceId, geofence.getName()); - } - } else { - iterator.remove(); - } - } else if (maintenanceId != 0) { - if (Context.getMaintenancesManager().checkItemPermission(userId, maintenanceId)) { - Maintenance maintenance = Context.getMaintenancesManager().getById(maintenanceId); - if (maintenance != null) { - maintenanceNames.put(maintenanceId, maintenance.getName()); - } - } else { - iterator.remove(); - } - } - } else { - iterator.remove(); - } - } - DeviceReport deviceEvents = new DeviceReport(); - Device device = Context.getIdentityManager().getById(deviceId); - deviceEvents.setDeviceName(device.getName()); - sheetNames.add(WorkbookUtil.createSafeSheetName(deviceEvents.getDeviceName())); - if (device.getGroupId() != 0) { - Group group = Context.getGroupsManager().getById(device.getGroupId()); - if (group != null) { - deviceEvents.setGroupName(group.getName()); - } - } - deviceEvents.setObjects(events); - devicesEvents.add(deviceEvents); - } - String templatePath = Context.getConfig().getString("report.templatesPath", - "templates/export/"); - try (InputStream inputStream = new FileInputStream(templatePath + "/events.xlsx")) { - org.jxls.common.Context jxlsContext = ReportUtils.initializeContext(userId); - jxlsContext.putVar("devices", devicesEvents); - jxlsContext.putVar("sheetNames", sheetNames); - jxlsContext.putVar("geofenceNames", geofenceNames); - jxlsContext.putVar("maintenanceNames", maintenanceNames); - jxlsContext.putVar("from", from); - jxlsContext.putVar("to", to); - ReportUtils.processTemplateWithSheets(inputStream, outputStream, jxlsContext); - } - } -} diff --git a/src/main/java/org/traccar/reports/EventsReportProvider.java b/src/main/java/org/traccar/reports/EventsReportProvider.java new file mode 100644 index 000000000..f252f28cc --- /dev/null +++ b/src/main/java/org/traccar/reports/EventsReportProvider.java @@ -0,0 +1,170 @@ +/* + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2018 Andrey Kunitsyn (andrey@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.reports; + +import org.apache.poi.ss.util.WorkbookUtil; +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.helper.model.DeviceUtil; +import org.traccar.model.Device; +import org.traccar.model.Event; +import org.traccar.model.Geofence; +import org.traccar.model.Group; +import org.traccar.model.Maintenance; +import org.traccar.model.Position; +import org.traccar.reports.common.ReportUtils; +import org.traccar.reports.model.DeviceReportSection; +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.Order; +import org.traccar.storage.query.Request; + +import jakarta.inject.Inject; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; + +public class EventsReportProvider { + + private final Config config; + private final ReportUtils reportUtils; + private final Storage storage; + + @Inject + public EventsReportProvider(Config config, ReportUtils reportUtils, Storage storage) { + this.config = config; + this.reportUtils = reportUtils; + this.storage = storage; + } + + private List<Event> getEvents(long deviceId, Date from, Date to) throws StorageException { + return storage.getObjects(Event.class, new Request( + new Columns.All(), + new Condition.And( + new Condition.Equals("deviceId", deviceId), + new Condition.Between("eventTime", "from", from, "to", to)), + new Order("eventTime"))); + } + + public Collection<Event> getObjects( + long userId, Collection<Long> deviceIds, Collection<Long> groupIds, + Collection<String> types, Date from, Date to) throws StorageException { + reportUtils.checkPeriodLimit(from, to); + + ArrayList<Event> result = new ArrayList<>(); + for (Device device: DeviceUtil.getAccessibleDevices(storage, userId, deviceIds, groupIds)) { + Collection<Event> events = getEvents(device.getId(), from, to); + boolean all = types.isEmpty() || types.contains(Event.ALL_EVENTS); + for (Event event : events) { + if (all || types.contains(event.getType())) { + long geofenceId = event.getGeofenceId(); + long maintenanceId = event.getMaintenanceId(); + if ((geofenceId == 0 || reportUtils.getObject(userId, Geofence.class, geofenceId) != null) + && (maintenanceId == 0 + || reportUtils.getObject(userId, Maintenance.class, maintenanceId) != null)) { + result.add(event); + } + } + } + } + return result; + } + + public void getExcel( + OutputStream outputStream, long userId, Collection<Long> deviceIds, Collection<Long> groupIds, + Collection<String> types, Date from, Date to) throws StorageException, IOException { + reportUtils.checkPeriodLimit(from, to); + + ArrayList<DeviceReportSection> devicesEvents = new ArrayList<>(); + ArrayList<String> sheetNames = new ArrayList<>(); + HashMap<Long, String> geofenceNames = new HashMap<>(); + HashMap<Long, String> maintenanceNames = new HashMap<>(); + HashMap<Long, Position> positions = new HashMap<>(); + for (Device device: DeviceUtil.getAccessibleDevices(storage, userId, deviceIds, groupIds)) { + Collection<Event> events = getEvents(device.getId(), from, to); + boolean all = types.isEmpty() || types.contains(Event.ALL_EVENTS); + for (Iterator<Event> iterator = events.iterator(); iterator.hasNext();) { + Event event = iterator.next(); + if (all || types.contains(event.getType())) { + long geofenceId = event.getGeofenceId(); + long maintenanceId = event.getMaintenanceId(); + if (geofenceId != 0) { + Geofence geofence = reportUtils.getObject(userId, Geofence.class, geofenceId); + if (geofence != null) { + geofenceNames.put(geofenceId, geofence.getName()); + } else { + iterator.remove(); + } + } else if (maintenanceId != 0) { + Maintenance maintenance = reportUtils.getObject(userId, Maintenance.class, maintenanceId); + if (maintenance != null) { + maintenanceNames.put(maintenanceId, maintenance.getName()); + } else { + iterator.remove(); + } + } + } else { + iterator.remove(); + } + } + for (Event event : events) { + long positionId = event.getPositionId(); + if (positionId > 0) { + Position position = storage.getObject(Position.class, new Request( + new Columns.All(), new Condition.Equals("id", positionId))); + positions.put(positionId, position); + } + } + DeviceReportSection deviceEvents = new DeviceReportSection(); + deviceEvents.setDeviceName(device.getName()); + sheetNames.add(WorkbookUtil.createSafeSheetName(deviceEvents.getDeviceName())); + if (device.getGroupId() > 0) { + Group group = storage.getObject(Group.class, new Request( + new Columns.All(), new Condition.Equals("id", device.getGroupId()))); + if (group != null) { + deviceEvents.setGroupName(group.getName()); + } + } + deviceEvents.setObjects(events); + devicesEvents.add(deviceEvents); + } + + File file = Paths.get(config.getString(Keys.TEMPLATES_ROOT), "export", "events.xlsx").toFile(); + try (InputStream inputStream = new FileInputStream(file)) { + var context = reportUtils.initializeContext(userId); + context.putVar("devices", devicesEvents); + context.putVar("sheetNames", sheetNames); + context.putVar("geofenceNames", geofenceNames); + context.putVar("maintenanceNames", maintenanceNames); + context.putVar("positions", positions); + context.putVar("from", from); + context.putVar("to", to); + reportUtils.processTemplateWithSheets(inputStream, outputStream, context); + } + } +} diff --git a/src/main/java/org/traccar/reports/GpxExportProvider.java b/src/main/java/org/traccar/reports/GpxExportProvider.java new file mode 100644 index 000000000..1c45b6416 --- /dev/null +++ b/src/main/java/org/traccar/reports/GpxExportProvider.java @@ -0,0 +1,76 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.reports; + +import org.traccar.helper.DateUtil; +import org.traccar.helper.model.PositionUtil; +import org.traccar.model.Device; +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 java.io.OutputStream; +import java.io.PrintWriter; +import java.util.Date; + +public class GpxExportProvider { + + private final Storage storage; + + @Inject + public GpxExportProvider(Storage storage) { + this.storage = storage; + } + + public void generate( + OutputStream outputStream, long deviceId, Date from, Date to) throws StorageException { + + var device = storage.getObject(Device.class, new Request( + new Columns.All(), new Condition.Equals("id", deviceId))); + var positions = PositionUtil.getPositions(storage, deviceId, from, to); + + try (PrintWriter writer = new PrintWriter(outputStream)) { + writer.print("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); + writer.print("<gpx version=\"1.0\">"); + writer.print("<trk>"); + writer.print("<name>"); + writer.print(device.getName()); + writer.print("</name>"); + writer.print("<trkseg>"); + positions.forEach(position -> { + writer.print("<trkpt lat=\""); + writer.print(position.getLatitude()); + writer.print("\" lon=\""); + writer.print(position.getLongitude()); + writer.print("\">"); + writer.print("<ele>"); + writer.print(position.getAltitude()); + writer.print("</ele>"); + writer.print("<time>"); + writer.print(DateUtil.formatDate(position.getFixTime())); + writer.print("</time>"); + writer.print("</trkpt>"); + }); + writer.print("</trkseg>"); + writer.print("</trk>"); + writer.print("</gpx>"); + } + } + +} diff --git a/src/main/java/org/traccar/reports/KmlExportProvider.java b/src/main/java/org/traccar/reports/KmlExportProvider.java new file mode 100644 index 000000000..24dca018c --- /dev/null +++ b/src/main/java/org/traccar/reports/KmlExportProvider.java @@ -0,0 +1,80 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.reports; + +import org.traccar.helper.model.PositionUtil; +import org.traccar.model.Device; +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 java.io.OutputStream; +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.stream.Collectors; + +public class KmlExportProvider { + + private final Storage storage; + + @Inject + public KmlExportProvider(Storage storage) { + this.storage = storage; + } + + public void generate( + OutputStream outputStream, long deviceId, Date from, Date to) throws StorageException { + + var device = storage.getObject(Device.class, new Request( + new Columns.All(), new Condition.Equals("id", deviceId))); + var positions = PositionUtil.getPositions(storage, deviceId, from, to); + + var dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); + + try (PrintWriter writer = new PrintWriter(outputStream)) { + writer.print("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); + writer.print("<kml xmlns=\"http://www.opengis.net/kml/2.2\">"); + writer.print("<Document>"); + writer.print("<name>"); + writer.print(device.getName()); + writer.print("</name>"); + writer.print("<Placemark>"); + writer.print("<name>"); + writer.print(dateFormat.format(from)); + writer.print(" - "); + writer.print(dateFormat.format(to)); + writer.print("</name>"); + writer.print("<LineString>"); + writer.print("<extrude>1</extrude>"); + writer.print("<tessellate>1</tessellate>"); + writer.print("<altitudeMode>absolute</altitudeMode>"); + writer.print("<coordinates>"); + writer.print(positions.stream() + .map((p -> String.format("%f,%f,%f", p.getLongitude(), p.getLatitude(), p.getAltitude()))) + .collect(Collectors.joining(" "))); + writer.print("</coordinates>"); + writer.print("</LineString>"); + writer.print("</Placemark>"); + writer.print("</Document>"); + writer.print("</kml>"); + } + } + +} diff --git a/src/main/java/org/traccar/reports/ReportUtils.java b/src/main/java/org/traccar/reports/ReportUtils.java deleted file mode 100644 index 58674beae..000000000 --- a/src/main/java/org/traccar/reports/ReportUtils.java +++ /dev/null @@ -1,368 +0,0 @@ -/* - * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org) - * Copyright 2016 - 2017 Andrey Kunitsyn (andrey@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.reports; - -import org.apache.velocity.tools.generic.DateTool; -import org.apache.velocity.tools.generic.NumberTool; -import org.jxls.area.Area; -import org.jxls.builder.xls.XlsCommentAreaBuilder; -import org.jxls.common.CellRef; -import org.jxls.formula.StandardFormulaProcessor; -import org.jxls.transform.Transformer; -import org.jxls.transform.poi.PoiTransformer; -import org.jxls.util.TransformerFactory; -import org.traccar.Context; -import org.traccar.config.Keys; -import org.traccar.database.DeviceManager; -import org.traccar.database.IdentityManager; -import org.traccar.handler.events.MotionEventHandler; -import org.traccar.helper.UnitsConverter; -import org.traccar.model.DeviceState; -import org.traccar.model.Driver; -import org.traccar.model.Event; -import org.traccar.model.Position; -import org.traccar.reports.model.BaseReport; -import org.traccar.reports.model.StopReport; -import org.traccar.reports.model.TripReport; -import org.traccar.reports.model.TripsConfig; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.TimeZone; - -public final class ReportUtils { - - private ReportUtils() { - } - - public static void checkPeriodLimit(Date from, Date to) { - long limit = Context.getConfig().getLong(Keys.REPORT_PERIOD_LIMIT) * 1000; - if (limit > 0 && to.getTime() - from.getTime() > limit) { - throw new IllegalArgumentException("Time period exceeds the limit"); - } - } - - public static String getDistanceUnit(long userId) { - return (String) Context.getPermissionsManager().lookupAttribute(userId, "distanceUnit", "km"); - } - - public static String getSpeedUnit(long userId) { - return (String) Context.getPermissionsManager().lookupAttribute(userId, "speedUnit", "kn"); - } - - public static String getVolumeUnit(long userId) { - return (String) Context.getPermissionsManager().lookupAttribute(userId, "volumeUnit", "ltr"); - } - - public static TimeZone getTimezone(long userId) { - String timezone = (String) Context.getPermissionsManager().lookupAttribute(userId, "timezone", null); - return timezone != null ? TimeZone.getTimeZone(timezone) : TimeZone.getDefault(); - } - - public static Collection<Long> getDeviceList(Collection<Long> deviceIds, Collection<Long> groupIds) { - Collection<Long> result = new ArrayList<>(deviceIds); - for (long groupId : groupIds) { - result.addAll(Context.getPermissionsManager().getGroupDevices(groupId)); - } - return result; - } - - public static double calculateDistance(Position firstPosition, Position lastPosition) { - return calculateDistance(firstPosition, lastPosition, true); - } - - public static double calculateDistance(Position firstPosition, Position lastPosition, boolean useOdometer) { - double distance = 0.0; - double firstOdometer = firstPosition.getDouble(Position.KEY_ODOMETER); - double lastOdometer = lastPosition.getDouble(Position.KEY_ODOMETER); - - if (useOdometer && firstOdometer != 0.0 && lastOdometer != 0.0) { - distance = lastOdometer - firstOdometer; - } else if (firstPosition.getAttributes().containsKey(Position.KEY_TOTAL_DISTANCE) - && lastPosition.getAttributes().containsKey(Position.KEY_TOTAL_DISTANCE)) { - distance = lastPosition.getDouble(Position.KEY_TOTAL_DISTANCE) - - firstPosition.getDouble(Position.KEY_TOTAL_DISTANCE); - } - return distance; - } - - public static double calculateFuel(Position firstPosition, Position lastPosition) { - - if (firstPosition.getAttributes().get(Position.KEY_FUEL_LEVEL) != null - && lastPosition.getAttributes().get(Position.KEY_FUEL_LEVEL) != null) { - - BigDecimal value = BigDecimal.valueOf(firstPosition.getDouble(Position.KEY_FUEL_LEVEL) - - lastPosition.getDouble(Position.KEY_FUEL_LEVEL)); - return value.setScale(1, RoundingMode.HALF_EVEN).doubleValue(); - } - return 0; - } - - public static String findDriver(Position firstPosition, Position lastPosition) { - if (firstPosition.getAttributes().containsKey(Position.KEY_DRIVER_UNIQUE_ID)) { - return firstPosition.getString(Position.KEY_DRIVER_UNIQUE_ID); - } else if (lastPosition.getAttributes().containsKey(Position.KEY_DRIVER_UNIQUE_ID)) { - return lastPosition.getString(Position.KEY_DRIVER_UNIQUE_ID); - } - return null; - } - - public static String findDriverName(String driverUniqueId) { - if (driverUniqueId != null && Context.getDriversManager() != null) { - Driver driver = Context.getDriversManager().getDriverByUniqueId(driverUniqueId); - if (driver != null) { - return driver.getName(); - } - } - return null; - } - - public static org.jxls.common.Context initializeContext(long userId) { - org.jxls.common.Context jxlsContext = PoiTransformer.createInitialContext(); - jxlsContext.putVar("distanceUnit", getDistanceUnit(userId)); - jxlsContext.putVar("speedUnit", getSpeedUnit(userId)); - jxlsContext.putVar("volumeUnit", getVolumeUnit(userId)); - jxlsContext.putVar("webUrl", Context.getVelocityEngine().getProperty("web.url")); - jxlsContext.putVar("dateTool", new DateTool()); - jxlsContext.putVar("numberTool", new NumberTool()); - jxlsContext.putVar("timezone", getTimezone(userId)); - jxlsContext.putVar("locale", Locale.getDefault()); - jxlsContext.putVar("bracketsRegex", "[\\{\\}\"]"); - return jxlsContext; - } - - public static void processTemplateWithSheets( - InputStream templateStream, OutputStream targetStream, - org.jxls.common.Context jxlsContext) throws IOException { - - Transformer transformer = TransformerFactory.createTransformer(templateStream, targetStream); - List<Area> xlsAreas = new XlsCommentAreaBuilder(transformer).build(); - for (Area xlsArea : xlsAreas) { - xlsArea.applyAt(new CellRef(xlsArea.getStartCellRef().getCellName()), jxlsContext); - xlsArea.setFormulaProcessor(new StandardFormulaProcessor()); - xlsArea.processFormulas(); - } - transformer.deleteSheet(xlsAreas.get(0).getStartCellRef().getSheetName()); - transformer.write(); - } - - private static TripReport calculateTrip( - ArrayList<Position> positions, int startIndex, int endIndex, boolean ignoreOdometer) { - - Position startTrip = positions.get(startIndex); - Position endTrip = positions.get(endIndex); - - double speedMax = 0; - for (int i = startIndex; i <= endIndex; i++) { - double speed = positions.get(i).getSpeed(); - if (speed > speedMax) { - speedMax = speed; - } - } - - TripReport trip = new TripReport(); - - long tripDuration = endTrip.getFixTime().getTime() - startTrip.getFixTime().getTime(); - long deviceId = startTrip.getDeviceId(); - trip.setDeviceId(deviceId); - trip.setDeviceName(Context.getIdentityManager().getById(deviceId).getName()); - - trip.setStartPositionId(startTrip.getId()); - trip.setStartLat(startTrip.getLatitude()); - trip.setStartLon(startTrip.getLongitude()); - trip.setStartTime(startTrip.getFixTime()); - String startAddress = startTrip.getAddress(); - if (startAddress == null && Context.getGeocoder() != null - && Context.getConfig().getBoolean(Keys.GEOCODER_ON_REQUEST)) { - startAddress = Context.getGeocoder().getAddress(startTrip.getLatitude(), startTrip.getLongitude(), null); - } - trip.setStartAddress(startAddress); - - trip.setEndPositionId(endTrip.getId()); - trip.setEndLat(endTrip.getLatitude()); - trip.setEndLon(endTrip.getLongitude()); - trip.setEndTime(endTrip.getFixTime()); - String endAddress = endTrip.getAddress(); - if (endAddress == null && Context.getGeocoder() != null - && Context.getConfig().getBoolean(Keys.GEOCODER_ON_REQUEST)) { - endAddress = Context.getGeocoder().getAddress(endTrip.getLatitude(), endTrip.getLongitude(), null); - } - trip.setEndAddress(endAddress); - - trip.setDistance(calculateDistance(startTrip, endTrip, !ignoreOdometer)); - trip.setDuration(tripDuration); - if (tripDuration > 0) { - trip.setAverageSpeed(UnitsConverter.knotsFromMps(trip.getDistance() * 1000 / tripDuration)); - } - trip.setMaxSpeed(speedMax); - trip.setSpentFuel(calculateFuel(startTrip, endTrip)); - - trip.setDriverUniqueId(findDriver(startTrip, endTrip)); - trip.setDriverName(findDriverName(trip.getDriverUniqueId())); - - if (!ignoreOdometer - && startTrip.getDouble(Position.KEY_ODOMETER) != 0 - && endTrip.getDouble(Position.KEY_ODOMETER) != 0) { - trip.setStartOdometer(startTrip.getDouble(Position.KEY_ODOMETER)); - trip.setEndOdometer(endTrip.getDouble(Position.KEY_ODOMETER)); - } else { - trip.setStartOdometer(startTrip.getDouble(Position.KEY_TOTAL_DISTANCE)); - trip.setEndOdometer(endTrip.getDouble(Position.KEY_TOTAL_DISTANCE)); - } - - return trip; - } - - private static StopReport calculateStop( - ArrayList<Position> positions, int startIndex, int endIndex, boolean ignoreOdometer) { - - Position startStop = positions.get(startIndex); - Position endStop = positions.get(endIndex); - - StopReport stop = new StopReport(); - - long deviceId = startStop.getDeviceId(); - stop.setDeviceId(deviceId); - stop.setDeviceName(Context.getIdentityManager().getById(deviceId).getName()); - - stop.setPositionId(startStop.getId()); - stop.setLatitude(startStop.getLatitude()); - stop.setLongitude(startStop.getLongitude()); - stop.setStartTime(startStop.getFixTime()); - String address = startStop.getAddress(); - if (address == null && Context.getGeocoder() != null - && Context.getConfig().getBoolean(Keys.GEOCODER_ON_REQUEST)) { - address = Context.getGeocoder().getAddress(stop.getLatitude(), stop.getLongitude(), null); - } - stop.setAddress(address); - - stop.setEndTime(endStop.getFixTime()); - - long stopDuration = endStop.getFixTime().getTime() - startStop.getFixTime().getTime(); - stop.setDuration(stopDuration); - stop.setSpentFuel(calculateFuel(startStop, endStop)); - - if (startStop.getAttributes().containsKey(Position.KEY_HOURS) - && endStop.getAttributes().containsKey(Position.KEY_HOURS)) { - stop.setEngineHours(endStop.getLong(Position.KEY_HOURS) - startStop.getLong(Position.KEY_HOURS)); - } - - if (!ignoreOdometer - && startStop.getDouble(Position.KEY_ODOMETER) != 0 - && endStop.getDouble(Position.KEY_ODOMETER) != 0) { - stop.setStartOdometer(startStop.getDouble(Position.KEY_ODOMETER)); - stop.setEndOdometer(endStop.getDouble(Position.KEY_ODOMETER)); - } else { - stop.setStartOdometer(startStop.getDouble(Position.KEY_TOTAL_DISTANCE)); - stop.setEndOdometer(endStop.getDouble(Position.KEY_TOTAL_DISTANCE)); - } - - return stop; - - } - - private static <T extends BaseReport> T calculateTripOrStop( - ArrayList<Position> positions, int startIndex, int endIndex, boolean ignoreOdometer, Class<T> reportClass) { - - if (reportClass.equals(TripReport.class)) { - return (T) calculateTrip(positions, startIndex, endIndex, ignoreOdometer); - } else { - return (T) calculateStop(positions, startIndex, endIndex, ignoreOdometer); - } - } - - private static boolean isMoving(ArrayList<Position> positions, int index, TripsConfig tripsConfig) { - if (tripsConfig.getMinimalNoDataDuration() > 0) { - boolean beforeGap = index < positions.size() - 1 - && positions.get(index + 1).getFixTime().getTime() - positions.get(index).getFixTime().getTime() - >= tripsConfig.getMinimalNoDataDuration(); - boolean afterGap = index > 0 - && positions.get(index).getFixTime().getTime() - positions.get(index - 1).getFixTime().getTime() - >= tripsConfig.getMinimalNoDataDuration(); - if (beforeGap || afterGap) { - return false; - } - } - if (positions.get(index).getAttributes().containsKey(Position.KEY_MOTION) - && positions.get(index).getAttributes().get(Position.KEY_MOTION) instanceof Boolean) { - return positions.get(index).getBoolean(Position.KEY_MOTION); - } else { - return positions.get(index).getSpeed() > tripsConfig.getSpeedThreshold(); - } - } - - public static <T extends BaseReport> Collection<T> detectTripsAndStops( - IdentityManager identityManager, DeviceManager deviceManager, - Collection<Position> positionCollection, - TripsConfig tripsConfig, boolean ignoreOdometer, Class<T> reportClass) { - - Collection<T> result = new ArrayList<>(); - - ArrayList<Position> positions = new ArrayList<>(positionCollection); - if (!positions.isEmpty()) { - boolean trips = reportClass.equals(TripReport.class); - MotionEventHandler motionHandler = new MotionEventHandler(identityManager, deviceManager, tripsConfig); - DeviceState deviceState = new DeviceState(); - deviceState.setMotionState(isMoving(positions, 0, tripsConfig)); - int startEventIndex = trips == deviceState.getMotionState() ? 0 : -1; - int startNoEventIndex = -1; - for (int i = 0; i < positions.size(); i++) { - Map<Event, Position> event = motionHandler.updateMotionState(deviceState, positions.get(i), - isMoving(positions, i, tripsConfig)); - if (startEventIndex == -1 - && (trips != deviceState.getMotionState() && deviceState.getMotionPosition() != null - || trips == deviceState.getMotionState() && event != null)) { - startEventIndex = i; - startNoEventIndex = -1; - } else if (trips != deviceState.getMotionState() && startEventIndex != -1 - && deviceState.getMotionPosition() == null && event == null) { - startEventIndex = -1; - } - if (startNoEventIndex == -1 - && (trips == deviceState.getMotionState() && deviceState.getMotionPosition() != null - || trips != deviceState.getMotionState() && event != null)) { - startNoEventIndex = i; - } else if (startNoEventIndex != -1 && deviceState.getMotionPosition() == null && event == null) { - startNoEventIndex = -1; - } - if (startEventIndex != -1 && startNoEventIndex != -1 && event != null - && trips != deviceState.getMotionState()) { - result.add(calculateTripOrStop(positions, startEventIndex, startNoEventIndex, - ignoreOdometer, reportClass)); - startEventIndex = -1; - } - } - if (startEventIndex != -1 && (startNoEventIndex != -1 || !trips)) { - result.add(calculateTripOrStop(positions, startEventIndex, - startNoEventIndex != -1 ? startNoEventIndex : positions.size() - 1, - ignoreOdometer, reportClass)); - } - } - - return result; - } - -} diff --git a/src/main/java/org/traccar/reports/Route.java b/src/main/java/org/traccar/reports/Route.java deleted file mode 100644 index 4a5edd295..000000000 --- a/src/main/java/org/traccar/reports/Route.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2016 Anton Tananaev (anton@traccar.org) - * Copyright 2016 Andrey Kunitsyn (andrey@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.reports; - -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; - -import org.apache.poi.ss.util.WorkbookUtil; -import org.traccar.Context; -import org.traccar.model.Device; -import org.traccar.model.Group; -import org.traccar.model.Position; -import org.traccar.reports.model.DeviceReport; -import org.traccar.storage.StorageException; - -public final class Route { - - private Route() { - } - - public static Collection<Position> getObjects(long userId, Collection<Long> deviceIds, Collection<Long> groupIds, - Date from, Date to) throws StorageException { - ReportUtils.checkPeriodLimit(from, to); - ArrayList<Position> result = new ArrayList<>(); - for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) { - Context.getPermissionsManager().checkDevice(userId, deviceId); - result.addAll(Context.getDataManager().getPositions(deviceId, from, to)); - } - return result; - } - - public static void getExcel(OutputStream outputStream, - long userId, Collection<Long> deviceIds, Collection<Long> groupIds, - Date from, Date to) throws StorageException, IOException { - ReportUtils.checkPeriodLimit(from, to); - ArrayList<DeviceReport> devicesRoutes = new ArrayList<>(); - ArrayList<String> sheetNames = new ArrayList<>(); - for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) { - Context.getPermissionsManager().checkDevice(userId, deviceId); - Collection<Position> positions = Context.getDataManager() - .getPositions(deviceId, from, to); - DeviceReport deviceRoutes = new DeviceReport(); - Device device = Context.getIdentityManager().getById(deviceId); - deviceRoutes.setDeviceName(device.getName()); - sheetNames.add(WorkbookUtil.createSafeSheetName(deviceRoutes.getDeviceName())); - if (device.getGroupId() != 0) { - Group group = Context.getGroupsManager().getById(device.getGroupId()); - if (group != null) { - deviceRoutes.setGroupName(group.getName()); - } - } - deviceRoutes.setObjects(positions); - devicesRoutes.add(deviceRoutes); - } - String templatePath = Context.getConfig().getString("report.templatesPath", - "templates/export/"); - try (InputStream inputStream = new FileInputStream(templatePath + "/route.xlsx")) { - org.jxls.common.Context jxlsContext = ReportUtils.initializeContext(userId); - jxlsContext.putVar("devices", devicesRoutes); - jxlsContext.putVar("sheetNames", sheetNames); - jxlsContext.putVar("from", from); - jxlsContext.putVar("to", to); - ReportUtils.processTemplateWithSheets(inputStream, outputStream, jxlsContext); - } - } -} diff --git a/src/main/java/org/traccar/reports/RouteReportProvider.java b/src/main/java/org/traccar/reports/RouteReportProvider.java new file mode 100644 index 000000000..d761fe1e5 --- /dev/null +++ b/src/main/java/org/traccar/reports/RouteReportProvider.java @@ -0,0 +1,113 @@ +/* + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2016 Andrey Kunitsyn (andrey@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.reports; + +import org.apache.poi.ss.util.WorkbookUtil; +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.helper.model.DeviceUtil; +import org.traccar.helper.model.PositionUtil; +import org.traccar.model.Device; +import org.traccar.model.Group; +import org.traccar.model.Position; +import org.traccar.reports.common.ReportUtils; +import org.traccar.reports.model.DeviceReportSection; +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 java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.Map; +import java.util.HashMap; + +public class RouteReportProvider { + + private final Config config; + private final ReportUtils reportUtils; + private final Storage storage; + + private final Map<String, Integer> namesCount = new HashMap<>(); + + @Inject + public RouteReportProvider(Config config, ReportUtils reportUtils, Storage storage) { + this.config = config; + this.reportUtils = reportUtils; + this.storage = storage; + } + + public Collection<Position> getObjects(long userId, Collection<Long> deviceIds, Collection<Long> groupIds, + Date from, Date to) throws StorageException { + reportUtils.checkPeriodLimit(from, to); + + ArrayList<Position> result = new ArrayList<>(); + for (Device device: DeviceUtil.getAccessibleDevices(storage, userId, deviceIds, groupIds)) { + result.addAll(PositionUtil.getPositions(storage, device.getId(), from, to)); + } + return result; + } + + + private String getUniqueSheetName(String key) { + namesCount.compute(key, (k, value) -> value == null ? 1 : (value + 1)); + return namesCount.get(key) > 1 ? key + '-' + namesCount.get(key) : key; + } + + public void getExcel(OutputStream outputStream, + long userId, Collection<Long> deviceIds, Collection<Long> groupIds, + Date from, Date to) throws StorageException, IOException { + reportUtils.checkPeriodLimit(from, to); + + ArrayList<DeviceReportSection> devicesRoutes = new ArrayList<>(); + ArrayList<String> sheetNames = new ArrayList<>(); + for (Device device: DeviceUtil.getAccessibleDevices(storage, userId, deviceIds, groupIds)) { + var positions = PositionUtil.getPositions(storage, device.getId(), from, to); + DeviceReportSection deviceRoutes = new DeviceReportSection(); + deviceRoutes.setDeviceName(device.getName()); + sheetNames.add(WorkbookUtil.createSafeSheetName(getUniqueSheetName(deviceRoutes.getDeviceName()))); + if (device.getGroupId() > 0) { + Group group = storage.getObject(Group.class, new Request( + new Columns.All(), new Condition.Equals("id", device.getGroupId()))); + if (group != null) { + deviceRoutes.setGroupName(group.getName()); + } + } + deviceRoutes.setObjects(positions); + devicesRoutes.add(deviceRoutes); + } + + File file = Paths.get(config.getString(Keys.TEMPLATES_ROOT), "export", "route.xlsx").toFile(); + try (InputStream inputStream = new FileInputStream(file)) { + var context = reportUtils.initializeContext(userId); + context.putVar("devices", devicesRoutes); + context.putVar("sheetNames", sheetNames); + context.putVar("from", from); + context.putVar("to", to); + reportUtils.processTemplateWithSheets(inputStream, outputStream, context); + } + } +} diff --git a/src/main/java/org/traccar/reports/Stops.java b/src/main/java/org/traccar/reports/Stops.java deleted file mode 100644 index 36a4a7549..000000000 --- a/src/main/java/org/traccar/reports/Stops.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2017 Anton Tananaev (anton@traccar.org) - * Copyright 2017 Andrey Kunitsyn (andrey@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.reports; - -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; - -import org.apache.poi.ss.util.WorkbookUtil; -import org.traccar.Context; -import org.traccar.Main; -import org.traccar.database.DeviceManager; -import org.traccar.database.IdentityManager; -import org.traccar.model.Device; -import org.traccar.model.Group; -import org.traccar.reports.model.DeviceReport; -import org.traccar.reports.model.StopReport; -import org.traccar.storage.StorageException; - -public final class Stops { - - private Stops() { - } - - private static Collection<StopReport> detectStops(long deviceId, Date from, Date to) throws StorageException { - boolean ignoreOdometer = Context.getDeviceManager() - .lookupAttributeBoolean(deviceId, "report.ignoreOdometer", false, false, true); - - IdentityManager identityManager = Main.getInjector().getInstance(IdentityManager.class); - DeviceManager deviceManager = Main.getInjector().getInstance(DeviceManager.class); - - return ReportUtils.detectTripsAndStops( - identityManager, deviceManager, Context.getDataManager().getPositions(deviceId, from, to), - Context.getTripsConfig(), ignoreOdometer, StopReport.class); - } - - public static Collection<StopReport> getObjects( - long userId, Collection<Long> deviceIds, Collection<Long> groupIds, - Date from, Date to) throws StorageException { - ReportUtils.checkPeriodLimit(from, to); - ArrayList<StopReport> result = new ArrayList<>(); - for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) { - Context.getPermissionsManager().checkDevice(userId, deviceId); - result.addAll(detectStops(deviceId, from, to)); - } - return result; - } - - public static void getExcel( - OutputStream outputStream, long userId, Collection<Long> deviceIds, Collection<Long> groupIds, - Date from, Date to) throws StorageException, IOException { - ReportUtils.checkPeriodLimit(from, to); - ArrayList<DeviceReport> devicesStops = new ArrayList<>(); - ArrayList<String> sheetNames = new ArrayList<>(); - for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) { - Context.getPermissionsManager().checkDevice(userId, deviceId); - Collection<StopReport> stops = detectStops(deviceId, from, to); - DeviceReport deviceStops = new DeviceReport(); - Device device = Context.getIdentityManager().getById(deviceId); - deviceStops.setDeviceName(device.getName()); - sheetNames.add(WorkbookUtil.createSafeSheetName(deviceStops.getDeviceName())); - if (device.getGroupId() != 0) { - Group group = Context.getGroupsManager().getById(device.getGroupId()); - if (group != null) { - deviceStops.setGroupName(group.getName()); - } - } - deviceStops.setObjects(stops); - devicesStops.add(deviceStops); - } - String templatePath = Context.getConfig().getString("report.templatesPath", - "templates/export/"); - try (InputStream inputStream = new FileInputStream(templatePath + "/stops.xlsx")) { - org.jxls.common.Context jxlsContext = ReportUtils.initializeContext(userId); - jxlsContext.putVar("devices", devicesStops); - jxlsContext.putVar("sheetNames", sheetNames); - jxlsContext.putVar("from", from); - jxlsContext.putVar("to", to); - ReportUtils.processTemplateWithSheets(inputStream, outputStream, jxlsContext); - } - } - -} diff --git a/src/main/java/org/traccar/reports/StopsReportProvider.java b/src/main/java/org/traccar/reports/StopsReportProvider.java new file mode 100644 index 000000000..2160fec0e --- /dev/null +++ b/src/main/java/org/traccar/reports/StopsReportProvider.java @@ -0,0 +1,104 @@ +/* + * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2017 Andrey Kunitsyn (andrey@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.reports; + +import org.apache.poi.ss.util.WorkbookUtil; +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.helper.model.DeviceUtil; +import org.traccar.model.Device; +import org.traccar.model.Group; +import org.traccar.reports.common.ReportUtils; +import org.traccar.reports.model.DeviceReportSection; +import org.traccar.reports.model.StopReportItem; +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 java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; + +public class StopsReportProvider { + + private final Config config; + private final ReportUtils reportUtils; + private final Storage storage; + + @Inject + public StopsReportProvider(Config config, ReportUtils reportUtils, Storage storage) { + this.config = config; + this.reportUtils = reportUtils; + this.storage = storage; + } + + public Collection<StopReportItem> getObjects( + long userId, Collection<Long> deviceIds, Collection<Long> groupIds, + Date from, Date to) throws StorageException { + reportUtils.checkPeriodLimit(from, to); + + ArrayList<StopReportItem> result = new ArrayList<>(); + for (Device device: DeviceUtil.getAccessibleDevices(storage, userId, deviceIds, groupIds)) { + result.addAll(reportUtils.detectTripsAndStops(device, from, to, StopReportItem.class)); + } + return result; + } + + public void getExcel( + OutputStream outputStream, long userId, Collection<Long> deviceIds, Collection<Long> groupIds, + Date from, Date to) throws StorageException, IOException { + reportUtils.checkPeriodLimit(from, to); + + ArrayList<DeviceReportSection> devicesStops = new ArrayList<>(); + ArrayList<String> sheetNames = new ArrayList<>(); + for (Device device: DeviceUtil.getAccessibleDevices(storage, userId, deviceIds, groupIds)) { + Collection<StopReportItem> stops = reportUtils.detectTripsAndStops(device, from, to, StopReportItem.class); + DeviceReportSection deviceStops = new DeviceReportSection(); + deviceStops.setDeviceName(device.getName()); + sheetNames.add(WorkbookUtil.createSafeSheetName(deviceStops.getDeviceName())); + if (device.getGroupId() > 0) { + Group group = storage.getObject(Group.class, new Request( + new Columns.All(), new Condition.Equals("id", device.getGroupId()))); + if (group != null) { + deviceStops.setGroupName(group.getName()); + } + } + deviceStops.setObjects(stops); + devicesStops.add(deviceStops); + } + + File file = Paths.get(config.getString(Keys.TEMPLATES_ROOT), "export", "stops.xlsx").toFile(); + try (InputStream inputStream = new FileInputStream(file)) { + var context = reportUtils.initializeContext(userId); + context.putVar("devices", devicesStops); + context.putVar("sheetNames", sheetNames); + context.putVar("from", from); + context.putVar("to", to); + reportUtils.processTemplateWithSheets(inputStream, outputStream, context); + } + } + +} diff --git a/src/main/java/org/traccar/reports/Summary.java b/src/main/java/org/traccar/reports/Summary.java deleted file mode 100644 index 4924af062..000000000 --- a/src/main/java/org/traccar/reports/Summary.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org) - * Copyright 2016 Andrey Kunitsyn (andrey@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.reports; - -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collection; -import java.util.Date; - -import org.jxls.util.JxlsHelper; -import org.traccar.Context; -import org.traccar.helper.UnitsConverter; -import org.traccar.model.Position; -import org.traccar.reports.model.SummaryReport; -import org.traccar.storage.StorageException; - -public final class Summary { - - private Summary() { - } - - private static SummaryReport calculateSummaryResult(long deviceId, Collection<Position> positions) { - SummaryReport result = new SummaryReport(); - result.setDeviceId(deviceId); - result.setDeviceName(Context.getIdentityManager().getById(deviceId).getName()); - if (positions != null && !positions.isEmpty()) { - Position firstPosition = null; - Position previousPosition = null; - for (Position position : positions) { - if (firstPosition == null) { - firstPosition = position; - } - previousPosition = position; - if (position.getSpeed() > result.getMaxSpeed()) { - result.setMaxSpeed(position.getSpeed()); - } - } - boolean ignoreOdometer = Context.getDeviceManager() - .lookupAttributeBoolean(deviceId, "report.ignoreOdometer", false, false, true); - result.setDistance(ReportUtils.calculateDistance(firstPosition, previousPosition, !ignoreOdometer)); - result.setSpentFuel(ReportUtils.calculateFuel(firstPosition, previousPosition)); - - long durationMilliseconds; - if (firstPosition.getAttributes().containsKey(Position.KEY_HOURS) - && previousPosition.getAttributes().containsKey(Position.KEY_HOURS)) { - durationMilliseconds = - previousPosition.getLong(Position.KEY_HOURS) - firstPosition.getLong(Position.KEY_HOURS); - result.setEngineHours(durationMilliseconds); - } else { - durationMilliseconds = - previousPosition.getFixTime().getTime() - firstPosition.getFixTime().getTime(); - } - - if (durationMilliseconds > 0) { - result.setAverageSpeed( - UnitsConverter.knotsFromMps(result.getDistance() * 1000 / durationMilliseconds)); - } - - if (!ignoreOdometer - && firstPosition.getDouble(Position.KEY_ODOMETER) != 0 - && previousPosition.getDouble(Position.KEY_ODOMETER) != 0) { - result.setStartOdometer(firstPosition.getDouble(Position.KEY_ODOMETER)); - result.setEndOdometer(previousPosition.getDouble(Position.KEY_ODOMETER)); - } else { - result.setStartOdometer(firstPosition.getDouble(Position.KEY_TOTAL_DISTANCE)); - result.setEndOdometer(previousPosition.getDouble(Position.KEY_TOTAL_DISTANCE)); - } - - result.setStartTime(firstPosition.getFixTime()); - result.setEndTime(previousPosition.getFixTime()); - } - return result; - } - - private static int getDay(long userId, Date date) { - Calendar calendar = Calendar.getInstance(ReportUtils.getTimezone(userId)); - calendar.setTime(date); - return calendar.get(Calendar.DAY_OF_MONTH); - } - - private static Collection<SummaryReport> calculateSummaryResults( - long userId, long deviceId, Date from, Date to, boolean daily) throws StorageException { - - ArrayList<Position> positions = new ArrayList<>(Context.getDataManager().getPositions(deviceId, from, to)); - - ArrayList<SummaryReport> results = new ArrayList<>(); - if (daily && !positions.isEmpty()) { - int startIndex = 0; - int startDay = getDay(userId, positions.iterator().next().getFixTime()); - for (int i = 0; i < positions.size(); i++) { - int currentDay = getDay(userId, positions.get(i).getFixTime()); - if (currentDay != startDay) { - results.add(calculateSummaryResult(deviceId, positions.subList(startIndex, i))); - startIndex = i; - startDay = currentDay; - } - } - results.add(calculateSummaryResult(deviceId, positions.subList(startIndex, positions.size()))); - } else { - results.add(calculateSummaryResult(deviceId, positions)); - } - - return results; - } - - public static Collection<SummaryReport> getObjects(long userId, Collection<Long> deviceIds, - Collection<Long> groupIds, Date from, Date to, boolean daily) throws StorageException { - ReportUtils.checkPeriodLimit(from, to); - ArrayList<SummaryReport> result = new ArrayList<>(); - for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) { - Context.getPermissionsManager().checkDevice(userId, deviceId); - result.addAll(calculateSummaryResults(userId, deviceId, from, to, daily)); - } - return result; - } - - public static void getExcel(OutputStream outputStream, - long userId, Collection<Long> deviceIds, Collection<Long> groupIds, - Date from, Date to, boolean daily) throws StorageException, IOException { - ReportUtils.checkPeriodLimit(from, to); - Collection<SummaryReport> summaries = getObjects(userId, deviceIds, groupIds, from, to, daily); - String templatePath = Context.getConfig().getString("report.templatesPath", - "templates/export/"); - try (InputStream inputStream = new FileInputStream(templatePath + "/summary.xlsx")) { - org.jxls.common.Context jxlsContext = ReportUtils.initializeContext(userId); - jxlsContext.putVar("summaries", summaries); - jxlsContext.putVar("from", from); - jxlsContext.putVar("to", to); - JxlsHelper.getInstance().setUseFastFormulaProcessor(false) - .processTemplate(inputStream, outputStream, jxlsContext); - } - } -} diff --git a/src/main/java/org/traccar/reports/SummaryReportProvider.java b/src/main/java/org/traccar/reports/SummaryReportProvider.java new file mode 100644 index 000000000..ffde0b067 --- /dev/null +++ b/src/main/java/org/traccar/reports/SummaryReportProvider.java @@ -0,0 +1,192 @@ +/* + * Copyright 2016 - 2023 Anton Tananaev (anton@traccar.org) + * Copyright 2016 Andrey Kunitsyn (andrey@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.reports; + +import org.jxls.util.JxlsHelper; +import org.traccar.api.security.PermissionsService; +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.helper.UnitsConverter; +import org.traccar.helper.model.DeviceUtil; +import org.traccar.helper.model.PositionUtil; +import org.traccar.helper.model.UserUtil; +import org.traccar.model.Device; +import org.traccar.model.Position; +import org.traccar.reports.common.ReportUtils; +import org.traccar.reports.model.SummaryReportItem; +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.Order; +import org.traccar.storage.query.Request; + +import jakarta.inject.Inject; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Paths; +import java.time.Duration; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; + +public class SummaryReportProvider { + + private final Config config; + private final ReportUtils reportUtils; + private final PermissionsService permissionsService; + private final Storage storage; + + @Inject + public SummaryReportProvider( + Config config, ReportUtils reportUtils, PermissionsService permissionsService, Storage storage) { + this.config = config; + this.reportUtils = reportUtils; + this.permissionsService = permissionsService; + this.storage = storage; + } + + private Position getEdgePosition(long deviceId, Date from, Date to, boolean end) throws StorageException { + return storage.getObject(Position.class, new Request( + new Columns.All(), + new Condition.And( + new Condition.Equals("deviceId", deviceId), + new Condition.Between("fixTime", "from", from, "to", to)), + new Order("fixTime", end, 1))); + } + + private Collection<SummaryReportItem> calculateDeviceResult( + Device device, Date from, Date to, boolean fast) throws StorageException { + + SummaryReportItem result = new SummaryReportItem(); + result.setDeviceId(device.getId()); + result.setDeviceName(device.getName()); + + Position first = null; + Position last = null; + if (fast) { + first = getEdgePosition(device.getId(), from, to, false); + last = getEdgePosition(device.getId(), from, to, true); + } else { + var positions = PositionUtil.getPositions(storage, device.getId(), from, to); + for (Position position : positions) { + if (first == null) { + first = position; + } + if (position.getSpeed() > result.getMaxSpeed()) { + result.setMaxSpeed(position.getSpeed()); + } + last = position; + } + } + + if (first != null && last != null) { + boolean ignoreOdometer = config.getBoolean(Keys.REPORT_IGNORE_ODOMETER); + result.setDistance(PositionUtil.calculateDistance(first, last, !ignoreOdometer)); + result.setSpentFuel(reportUtils.calculateFuel(first, last)); + + long durationMilliseconds; + if (first.hasAttribute(Position.KEY_HOURS) && last.hasAttribute(Position.KEY_HOURS)) { + durationMilliseconds = last.getLong(Position.KEY_HOURS) - first.getLong(Position.KEY_HOURS); + result.setEngineHours(durationMilliseconds); + } else { + durationMilliseconds = last.getFixTime().getTime() - first.getFixTime().getTime(); + } + + if (durationMilliseconds > 0) { + result.setAverageSpeed(UnitsConverter.knotsFromMps(result.getDistance() * 1000 / durationMilliseconds)); + } + + if (!ignoreOdometer + && first.getDouble(Position.KEY_ODOMETER) != 0 && last.getDouble(Position.KEY_ODOMETER) != 0) { + result.setStartOdometer(first.getDouble(Position.KEY_ODOMETER)); + result.setEndOdometer(last.getDouble(Position.KEY_ODOMETER)); + } else { + result.setStartOdometer(first.getDouble(Position.KEY_TOTAL_DISTANCE)); + result.setEndOdometer(last.getDouble(Position.KEY_TOTAL_DISTANCE)); + } + + result.setStartTime(first.getFixTime()); + result.setEndTime(last.getFixTime()); + return List.of(result); + } + + return List.of(); + } + + private Collection<SummaryReportItem> calculateDeviceResults( + Device device, ZonedDateTime from, ZonedDateTime to, boolean daily) throws StorageException { + + boolean fast = Duration.between(from, to).toSeconds() > config.getLong(Keys.REPORT_FAST_THRESHOLD); + var results = new ArrayList<SummaryReportItem>(); + if (daily) { + while (from.truncatedTo(ChronoUnit.DAYS).isBefore(to.truncatedTo(ChronoUnit.DAYS))) { + ZonedDateTime fromDay = from.truncatedTo(ChronoUnit.DAYS); + ZonedDateTime nextDay = fromDay.plus(1, ChronoUnit.DAYS); + results.addAll(calculateDeviceResult( + device, Date.from(from.toInstant()), Date.from(nextDay.toInstant()), fast)); + from = nextDay; + } + results.addAll(calculateDeviceResult(device, Date.from(from.toInstant()), Date.from(to.toInstant()), fast)); + } else { + results.addAll(calculateDeviceResult(device, Date.from(from.toInstant()), Date.from(to.toInstant()), fast)); + } + return results; + } + + public Collection<SummaryReportItem> getObjects( + long userId, Collection<Long> deviceIds, Collection<Long> groupIds, + Date from, Date to, boolean daily) throws StorageException { + reportUtils.checkPeriodLimit(from, to); + + var tz = UserUtil.getTimezone(permissionsService.getServer(), permissionsService.getUser(userId)).toZoneId(); + + ArrayList<SummaryReportItem> result = new ArrayList<>(); + for (Device device: DeviceUtil.getAccessibleDevices(storage, userId, deviceIds, groupIds)) { + var deviceResults = calculateDeviceResults( + device, from.toInstant().atZone(tz), to.toInstant().atZone(tz), daily); + for (SummaryReportItem summaryReport : deviceResults) { + if (summaryReport.getStartTime() != null && summaryReport.getEndTime() != null) { + result.add(summaryReport); + } + } + } + return result; + } + + public void getExcel(OutputStream outputStream, + long userId, Collection<Long> deviceIds, Collection<Long> groupIds, + Date from, Date to, boolean daily) throws StorageException, IOException { + Collection<SummaryReportItem> summaries = getObjects(userId, deviceIds, groupIds, from, to, daily); + + File file = Paths.get(config.getString(Keys.TEMPLATES_ROOT), "export", "summary.xlsx").toFile(); + try (InputStream inputStream = new FileInputStream(file)) { + var context = reportUtils.initializeContext(userId); + context.putVar("summaries", summaries); + context.putVar("from", from); + context.putVar("to", to); + JxlsHelper.getInstance().setUseFastFormulaProcessor(false) + .processTemplate(inputStream, outputStream, context); + } + } +} diff --git a/src/main/java/org/traccar/reports/Trips.java b/src/main/java/org/traccar/reports/Trips.java deleted file mode 100644 index 1461b869e..000000000 --- a/src/main/java/org/traccar/reports/Trips.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2016 Anton Tananaev (anton@traccar.org) - * Copyright 2016 Andrey Kunitsyn (andrey@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.reports; - -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; - -import org.apache.poi.ss.util.WorkbookUtil; -import org.traccar.Context; -import org.traccar.Main; -import org.traccar.database.DeviceManager; -import org.traccar.database.IdentityManager; -import org.traccar.model.Device; -import org.traccar.model.Group; -import org.traccar.reports.model.DeviceReport; -import org.traccar.reports.model.TripReport; -import org.traccar.storage.StorageException; - -public final class Trips { - - private Trips() { - } - - private static Collection<TripReport> detectTrips(long deviceId, Date from, Date to) throws StorageException { - boolean ignoreOdometer = Context.getDeviceManager() - .lookupAttributeBoolean(deviceId, "report.ignoreOdometer", false, false, true); - - IdentityManager identityManager = Main.getInjector().getInstance(IdentityManager.class); - DeviceManager deviceManager = Main.getInjector().getInstance(DeviceManager.class); - - return ReportUtils.detectTripsAndStops( - identityManager, deviceManager, Context.getDataManager().getPositions(deviceId, from, to), - Context.getTripsConfig(), ignoreOdometer, TripReport.class); - } - - public static Collection<TripReport> getObjects(long userId, Collection<Long> deviceIds, Collection<Long> groupIds, - Date from, Date to) throws StorageException { - ReportUtils.checkPeriodLimit(from, to); - ArrayList<TripReport> result = new ArrayList<>(); - for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) { - Context.getPermissionsManager().checkDevice(userId, deviceId); - result.addAll(detectTrips(deviceId, from, to)); - } - return result; - } - - public static void getExcel(OutputStream outputStream, - long userId, Collection<Long> deviceIds, Collection<Long> groupIds, - Date from, Date to) throws StorageException, IOException { - ReportUtils.checkPeriodLimit(from, to); - ArrayList<DeviceReport> devicesTrips = new ArrayList<>(); - ArrayList<String> sheetNames = new ArrayList<>(); - for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) { - Context.getPermissionsManager().checkDevice(userId, deviceId); - Collection<TripReport> trips = detectTrips(deviceId, from, to); - DeviceReport deviceTrips = new DeviceReport(); - Device device = Context.getIdentityManager().getById(deviceId); - deviceTrips.setDeviceName(device.getName()); - sheetNames.add(WorkbookUtil.createSafeSheetName(deviceTrips.getDeviceName())); - if (device.getGroupId() != 0) { - Group group = Context.getGroupsManager().getById(device.getGroupId()); - if (group != null) { - deviceTrips.setGroupName(group.getName()); - } - } - deviceTrips.setObjects(trips); - devicesTrips.add(deviceTrips); - } - String templatePath = Context.getConfig().getString("report.templatesPath", - "templates/export/"); - try (InputStream inputStream = new FileInputStream(templatePath + "/trips.xlsx")) { - org.jxls.common.Context jxlsContext = ReportUtils.initializeContext(userId); - jxlsContext.putVar("devices", devicesTrips); - jxlsContext.putVar("sheetNames", sheetNames); - jxlsContext.putVar("from", from); - jxlsContext.putVar("to", to); - ReportUtils.processTemplateWithSheets(inputStream, outputStream, jxlsContext); - } - } - -} diff --git a/src/main/java/org/traccar/reports/TripsReportProvider.java b/src/main/java/org/traccar/reports/TripsReportProvider.java new file mode 100644 index 000000000..9ff7232af --- /dev/null +++ b/src/main/java/org/traccar/reports/TripsReportProvider.java @@ -0,0 +1,104 @@ +/* + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2016 Andrey Kunitsyn (andrey@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.reports; + +import org.apache.poi.ss.util.WorkbookUtil; +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.helper.model.DeviceUtil; +import org.traccar.model.Device; +import org.traccar.model.Group; +import org.traccar.reports.common.ReportUtils; +import org.traccar.reports.model.DeviceReportSection; +import org.traccar.reports.model.TripReportItem; +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 java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; + +public class TripsReportProvider { + + private final Config config; + private final ReportUtils reportUtils; + private final Storage storage; + + @Inject + public TripsReportProvider(Config config, ReportUtils reportUtils, Storage storage) { + this.config = config; + this.reportUtils = reportUtils; + this.storage = storage; + } + + public Collection<TripReportItem> getObjects( + long userId, Collection<Long> deviceIds, Collection<Long> groupIds, + Date from, Date to) throws StorageException { + reportUtils.checkPeriodLimit(from, to); + + ArrayList<TripReportItem> result = new ArrayList<>(); + for (Device device: DeviceUtil.getAccessibleDevices(storage, userId, deviceIds, groupIds)) { + result.addAll(reportUtils.detectTripsAndStops(device, from, to, TripReportItem.class)); + } + return result; + } + + public void getExcel(OutputStream outputStream, + long userId, Collection<Long> deviceIds, Collection<Long> groupIds, + Date from, Date to) throws StorageException, IOException { + reportUtils.checkPeriodLimit(from, to); + + ArrayList<DeviceReportSection> devicesTrips = new ArrayList<>(); + ArrayList<String> sheetNames = new ArrayList<>(); + for (Device device: DeviceUtil.getAccessibleDevices(storage, userId, deviceIds, groupIds)) { + Collection<TripReportItem> trips = reportUtils.detectTripsAndStops(device, from, to, TripReportItem.class); + DeviceReportSection deviceTrips = new DeviceReportSection(); + deviceTrips.setDeviceName(device.getName()); + sheetNames.add(WorkbookUtil.createSafeSheetName(deviceTrips.getDeviceName())); + if (device.getGroupId() > 0) { + Group group = storage.getObject(Group.class, new Request( + new Columns.All(), new Condition.Equals("id", device.getGroupId()))); + if (group != null) { + deviceTrips.setGroupName(group.getName()); + } + } + deviceTrips.setObjects(trips); + devicesTrips.add(deviceTrips); + } + + File file = Paths.get(config.getString(Keys.TEMPLATES_ROOT), "export", "trips.xlsx").toFile(); + try (InputStream inputStream = new FileInputStream(file)) { + var context = reportUtils.initializeContext(userId); + context.putVar("devices", devicesTrips); + context.putVar("sheetNames", sheetNames); + context.putVar("from", from); + context.putVar("to", to); + reportUtils.processTemplateWithSheets(inputStream, outputStream, context); + } + } + +} diff --git a/src/main/java/org/traccar/reports/common/ExpressionEvaluatorFactory.java b/src/main/java/org/traccar/reports/common/ExpressionEvaluatorFactory.java new file mode 100644 index 000000000..8b139a572 --- /dev/null +++ b/src/main/java/org/traccar/reports/common/ExpressionEvaluatorFactory.java @@ -0,0 +1,58 @@ +package org.traccar.reports.common; + +import org.apache.commons.jexl3.JexlBuilder; +import org.apache.commons.jexl3.introspection.JexlPermissions; +import org.jxls.expression.ExpressionEvaluator; +import org.jxls.expression.JexlExpressionEvaluator; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +public class ExpressionEvaluatorFactory implements org.jxls.expression.ExpressionEvaluatorFactory { + + private final JexlPermissions permissions = new JexlPermissions() { + @Override + public boolean allow(Package pack) { + return true; + } + + @Override + public boolean allow(Class<?> clazz) { + return true; + } + + @Override + public boolean allow(Constructor<?> ctor) { + return true; + } + + @Override + public boolean allow(Method method) { + return true; + } + + @Override + public boolean allow(Field field) { + return true; + } + + @Override + public JexlPermissions compose(String... src) { + return this; + } + }; + + @Override + public ExpressionEvaluator createExpressionEvaluator(String expression) { + JexlExpressionEvaluator expressionEvaluator = expression == null + ? new JexlExpressionEvaluator() + : new JexlExpressionEvaluator(expression); + expressionEvaluator.setJexlEngine(new JexlBuilder() + .silent(true) + .strict(false) + .permissions(permissions) + .create()); + return expressionEvaluator; + } +} diff --git a/src/main/java/org/traccar/reports/common/ReportExecutor.java b/src/main/java/org/traccar/reports/common/ReportExecutor.java new file mode 100644 index 000000000..aed4b8c23 --- /dev/null +++ b/src/main/java/org/traccar/reports/common/ReportExecutor.java @@ -0,0 +1,25 @@ +/* + * 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.reports.common; + +import org.traccar.storage.StorageException; + +import java.io.IOException; +import java.io.OutputStream; + +public interface ReportExecutor { + void execute(OutputStream stream) throws StorageException, IOException; +} diff --git a/src/main/java/org/traccar/reports/common/ReportMailer.java b/src/main/java/org/traccar/reports/common/ReportMailer.java new file mode 100644 index 000000000..9fb30fe9f --- /dev/null +++ b/src/main/java/org/traccar/reports/common/ReportMailer.java @@ -0,0 +1,65 @@ +/* + * 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.reports.common; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.api.security.PermissionsService; +import org.traccar.mail.MailManager; +import org.traccar.model.User; +import org.traccar.storage.StorageException; + +import jakarta.activation.DataHandler; +import jakarta.inject.Inject; +import jakarta.mail.MessagingException; +import jakarta.mail.internet.MimeBodyPart; +import jakarta.mail.util.ByteArrayDataSource; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public class ReportMailer { + + private static final Logger LOGGER = LoggerFactory.getLogger(ReportMailer.class); + + private final PermissionsService permissionsService; + private final MailManager mailManager; + + @Inject + public ReportMailer(PermissionsService permissionsService, MailManager mailManager) { + this.permissionsService = permissionsService; + this.mailManager = mailManager; + } + + public void sendAsync(long userId, ReportExecutor executor) { + new Thread(() -> { + try { + var stream = new ByteArrayOutputStream(); + executor.execute(stream); + + MimeBodyPart attachment = new MimeBodyPart(); + attachment.setFileName("report.xlsx"); + attachment.setDataHandler(new DataHandler(new ByteArrayDataSource( + stream.toByteArray(), "application/octet-stream"))); + + User user = permissionsService.getUser(userId); + mailManager.sendMessage(user, false, "Report", "The report is in the attachment.", attachment); + } catch (StorageException | IOException | MessagingException e) { + LOGGER.warn("Email report failed", e); + } + }).start(); + } + +} diff --git a/src/main/java/org/traccar/reports/common/ReportUtils.java b/src/main/java/org/traccar/reports/common/ReportUtils.java new file mode 100644 index 000000000..43db82708 --- /dev/null +++ b/src/main/java/org/traccar/reports/common/ReportUtils.java @@ -0,0 +1,406 @@ +/* + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2017 Andrey Kunitsyn (andrey@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.reports.common; + +import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.tools.generic.DateTool; +import org.apache.velocity.tools.generic.NumberTool; +import org.jxls.area.Area; +import org.jxls.builder.xls.XlsCommentAreaBuilder; +import org.jxls.common.CellRef; +import org.jxls.formula.StandardFormulaProcessor; +import org.jxls.transform.Transformer; +import org.jxls.transform.poi.PoiTransformer; +import org.jxls.util.TransformerFactory; +import org.traccar.api.security.PermissionsService; +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.geocoder.Geocoder; +import org.traccar.helper.UnitsConverter; +import org.traccar.helper.model.AttributeUtil; +import org.traccar.helper.model.PositionUtil; +import org.traccar.helper.model.UserUtil; +import org.traccar.model.BaseModel; +import org.traccar.model.Device; +import org.traccar.model.Driver; +import org.traccar.model.Event; +import org.traccar.model.Position; +import org.traccar.model.User; +import org.traccar.reports.model.BaseReportItem; +import org.traccar.reports.model.StopReportItem; +import org.traccar.reports.model.TripReportItem; +import org.traccar.session.state.MotionProcessor; +import org.traccar.session.state.MotionState; +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.Order; +import org.traccar.storage.query.Request; + +import jakarta.annotation.Nullable; +import jakarta.inject.Inject; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.stream.Collectors; + +public class ReportUtils { + + private final Config config; + private final Storage storage; + private final PermissionsService permissionsService; + private final VelocityEngine velocityEngine; + private final Geocoder geocoder; + + @Inject + public ReportUtils( + Config config, Storage storage, PermissionsService permissionsService, + VelocityEngine velocityEngine, @Nullable Geocoder geocoder) { + this.config = config; + this.storage = storage; + this.permissionsService = permissionsService; + this.velocityEngine = velocityEngine; + this.geocoder = geocoder; + } + + public <T extends BaseModel> T getObject( + long userId, Class<T> clazz, long objectId) throws StorageException, SecurityException { + return storage.getObject(clazz, new Request( + new Columns.All(), + new Condition.And( + new Condition.Equals("id", objectId), + new Condition.Permission(User.class, userId, clazz)))); + } + + public void checkPeriodLimit(Date from, Date to) { + long limit = config.getLong(Keys.REPORT_PERIOD_LIMIT) * 1000; + if (limit > 0 && to.getTime() - from.getTime() > limit) { + throw new IllegalArgumentException("Time period exceeds the limit"); + } + } + + public double calculateFuel(Position firstPosition, Position lastPosition) { + + if (firstPosition.getAttributes().get(Position.KEY_FUEL_LEVEL) != null + && lastPosition.getAttributes().get(Position.KEY_FUEL_LEVEL) != null) { + + BigDecimal value = BigDecimal.valueOf(firstPosition.getDouble(Position.KEY_FUEL_LEVEL) + - lastPosition.getDouble(Position.KEY_FUEL_LEVEL)); + return value.setScale(1, RoundingMode.HALF_EVEN).doubleValue(); + } + return 0; + } + + public String findDriver(Position firstPosition, Position lastPosition) { + if (firstPosition.hasAttribute(Position.KEY_DRIVER_UNIQUE_ID)) { + return firstPosition.getString(Position.KEY_DRIVER_UNIQUE_ID); + } else if (lastPosition.hasAttribute(Position.KEY_DRIVER_UNIQUE_ID)) { + return lastPosition.getString(Position.KEY_DRIVER_UNIQUE_ID); + } + return null; + } + + public String findDriverName(String driverUniqueId) throws StorageException { + if (driverUniqueId != null) { + Driver driver = storage.getObject(Driver.class, new Request( + new Columns.All(), + new Condition.Equals("uniqueId", driverUniqueId))); + if (driver != null) { + return driver.getName(); + } + } + return null; + } + + public org.jxls.common.Context initializeContext(long userId) throws StorageException { + var server = permissionsService.getServer(); + var user = permissionsService.getUser(userId); + var context = PoiTransformer.createInitialContext(); + context.putVar("distanceUnit", UserUtil.getDistanceUnit(server, user)); + context.putVar("speedUnit", UserUtil.getSpeedUnit(server, user)); + context.putVar("volumeUnit", UserUtil.getVolumeUnit(server, user)); + context.putVar("webUrl", velocityEngine.getProperty("web.url")); + context.putVar("dateTool", new DateTool()); + context.putVar("numberTool", new NumberTool()); + context.putVar("timezone", UserUtil.getTimezone(server, user)); + context.putVar("locale", Locale.getDefault()); + context.putVar("bracketsRegex", "[\\{\\}\"]"); + return context; + } + + public void processTemplateWithSheets( + InputStream templateStream, OutputStream targetStream, org.jxls.common.Context context) throws IOException { + + Transformer transformer = TransformerFactory.createTransformer(templateStream, targetStream); + List<Area> xlsAreas = new XlsCommentAreaBuilder(transformer).build(); + for (Area xlsArea : xlsAreas) { + xlsArea.applyAt(new CellRef(xlsArea.getStartCellRef().getCellName()), context); + xlsArea.setFormulaProcessor(new StandardFormulaProcessor()); + xlsArea.processFormulas(); + } + transformer.deleteSheet(xlsAreas.get(0).getStartCellRef().getSheetName()); + transformer.write(); + } + + private TripReportItem calculateTrip( + Device device, Position startTrip, Position endTrip, double maxSpeed, + boolean ignoreOdometer) throws StorageException { + + TripReportItem trip = new TripReportItem(); + + long tripDuration = endTrip.getFixTime().getTime() - startTrip.getFixTime().getTime(); + long deviceId = startTrip.getDeviceId(); + trip.setDeviceId(deviceId); + trip.setDeviceName(device.getName()); + + trip.setStartPositionId(startTrip.getId()); + trip.setStartLat(startTrip.getLatitude()); + trip.setStartLon(startTrip.getLongitude()); + trip.setStartTime(startTrip.getFixTime()); + String startAddress = startTrip.getAddress(); + if (startAddress == null && geocoder != null && config.getBoolean(Keys.GEOCODER_ON_REQUEST)) { + startAddress = geocoder.getAddress(startTrip.getLatitude(), startTrip.getLongitude(), null); + } + trip.setStartAddress(startAddress); + + trip.setEndPositionId(endTrip.getId()); + trip.setEndLat(endTrip.getLatitude()); + trip.setEndLon(endTrip.getLongitude()); + trip.setEndTime(endTrip.getFixTime()); + String endAddress = endTrip.getAddress(); + if (endAddress == null && geocoder != null && config.getBoolean(Keys.GEOCODER_ON_REQUEST)) { + endAddress = geocoder.getAddress(endTrip.getLatitude(), endTrip.getLongitude(), null); + } + trip.setEndAddress(endAddress); + + trip.setDistance(PositionUtil.calculateDistance(startTrip, endTrip, !ignoreOdometer)); + trip.setDuration(tripDuration); + if (tripDuration > 0) { + trip.setAverageSpeed(UnitsConverter.knotsFromMps(trip.getDistance() * 1000 / tripDuration)); + } + trip.setMaxSpeed(maxSpeed); + trip.setSpentFuel(calculateFuel(startTrip, endTrip)); + + trip.setDriverUniqueId(findDriver(startTrip, endTrip)); + trip.setDriverName(findDriverName(trip.getDriverUniqueId())); + + if (!ignoreOdometer + && startTrip.getDouble(Position.KEY_ODOMETER) != 0 + && endTrip.getDouble(Position.KEY_ODOMETER) != 0) { + trip.setStartOdometer(startTrip.getDouble(Position.KEY_ODOMETER)); + trip.setEndOdometer(endTrip.getDouble(Position.KEY_ODOMETER)); + } else { + trip.setStartOdometer(startTrip.getDouble(Position.KEY_TOTAL_DISTANCE)); + trip.setEndOdometer(endTrip.getDouble(Position.KEY_TOTAL_DISTANCE)); + } + + return trip; + } + + private StopReportItem calculateStop( + Device device, Position startStop, Position endStop, boolean ignoreOdometer) { + + StopReportItem stop = new StopReportItem(); + + long deviceId = startStop.getDeviceId(); + stop.setDeviceId(deviceId); + stop.setDeviceName(device.getName()); + + stop.setPositionId(startStop.getId()); + stop.setLatitude(startStop.getLatitude()); + stop.setLongitude(startStop.getLongitude()); + stop.setStartTime(startStop.getFixTime()); + String address = startStop.getAddress(); + if (address == null && geocoder != null && config.getBoolean(Keys.GEOCODER_ON_REQUEST)) { + address = geocoder.getAddress(stop.getLatitude(), stop.getLongitude(), null); + } + stop.setAddress(address); + + stop.setEndTime(endStop.getFixTime()); + + long stopDuration = endStop.getFixTime().getTime() - startStop.getFixTime().getTime(); + stop.setDuration(stopDuration); + stop.setSpentFuel(calculateFuel(startStop, endStop)); + + if (startStop.hasAttribute(Position.KEY_HOURS) && endStop.hasAttribute(Position.KEY_HOURS)) { + stop.setEngineHours(endStop.getLong(Position.KEY_HOURS) - startStop.getLong(Position.KEY_HOURS)); + } + + if (!ignoreOdometer + && startStop.getDouble(Position.KEY_ODOMETER) != 0 + && endStop.getDouble(Position.KEY_ODOMETER) != 0) { + stop.setStartOdometer(startStop.getDouble(Position.KEY_ODOMETER)); + stop.setEndOdometer(endStop.getDouble(Position.KEY_ODOMETER)); + } else { + stop.setStartOdometer(startStop.getDouble(Position.KEY_TOTAL_DISTANCE)); + stop.setEndOdometer(endStop.getDouble(Position.KEY_TOTAL_DISTANCE)); + } + + return stop; + + } + + @SuppressWarnings("unchecked") + private <T extends BaseReportItem> T calculateTripOrStop( + Device device, Position startPosition, Position endPosition, double maxSpeed, + boolean ignoreOdometer, Class<T> reportClass) throws StorageException { + + if (reportClass.equals(TripReportItem.class)) { + return (T) calculateTrip(device, startPosition, endPosition, maxSpeed, ignoreOdometer); + } else { + return (T) calculateStop(device, startPosition, endPosition, ignoreOdometer); + } + } + + private boolean isMoving(List<Position> positions, int index, TripsConfig tripsConfig) { + if (tripsConfig.getMinimalNoDataDuration() > 0) { + boolean beforeGap = index < positions.size() - 1 + && positions.get(index + 1).getFixTime().getTime() - positions.get(index).getFixTime().getTime() + >= tripsConfig.getMinimalNoDataDuration(); + boolean afterGap = index > 0 + && positions.get(index).getFixTime().getTime() - positions.get(index - 1).getFixTime().getTime() + >= tripsConfig.getMinimalNoDataDuration(); + if (beforeGap || afterGap) { + return false; + } + } + return positions.get(index).getBoolean(Position.KEY_MOTION); + } + + public <T extends BaseReportItem> List<T> detectTripsAndStops( + Device device, Date from, Date to, Class<T> reportClass) throws StorageException { + + long threshold = config.getLong(Keys.REPORT_FAST_THRESHOLD); + if (Duration.between(from.toInstant(), to.toInstant()).toSeconds() > threshold) { + return fastTripsAndStops(device, from, to, reportClass); + } else { + return slowTripsAndStops(device, from, to, reportClass); + } + } + + public <T extends BaseReportItem> List<T> slowTripsAndStops( + Device device, Date from, Date to, Class<T> reportClass) throws StorageException { + + List<T> result = new ArrayList<>(); + TripsConfig tripsConfig = new TripsConfig( + new AttributeUtil.StorageProvider(config, storage, permissionsService, device)); + boolean ignoreOdometer = config.getBoolean(Keys.REPORT_IGNORE_ODOMETER); + + var positions = PositionUtil.getPositions(storage, device.getId(), from, to); + if (!positions.isEmpty()) { + boolean trips = reportClass.equals(TripReportItem.class); + + MotionState motionState = new MotionState(); + boolean initialValue = isMoving(positions, 0, tripsConfig); + motionState.setMotionStreak(initialValue); + motionState.setMotionState(initialValue); + + boolean detected = trips == motionState.getMotionState(); + double maxSpeed = 0; + int startEventIndex = detected ? 0 : -1; + int startNoEventIndex = -1; + for (int i = 0; i < positions.size(); i++) { + boolean motion = isMoving(positions, i, tripsConfig); + if (motionState.getMotionState() != motion) { + if (motion == trips) { + if (!detected) { + startEventIndex = i; + maxSpeed = positions.get(i).getSpeed(); + } + startNoEventIndex = -1; + } else { + startNoEventIndex = i; + } + } else { + maxSpeed = Math.max(maxSpeed, positions.get(i).getSpeed()); + } + + MotionProcessor.updateState(motionState, positions.get(i), motion, tripsConfig); + if (motionState.getEvent() != null) { + if (motion == trips) { + detected = true; + startNoEventIndex = -1; + } else if (startEventIndex >= 0 && startNoEventIndex >= 0) { + result.add(calculateTripOrStop( + device, positions.get(startEventIndex), positions.get(startNoEventIndex), + maxSpeed, ignoreOdometer, reportClass)); + detected = false; + startEventIndex = -1; + startNoEventIndex = -1; + } + } + } + if (detected & startEventIndex >= 0 && startEventIndex < positions.size() - 1) { + int endIndex = startNoEventIndex >= 0 ? startNoEventIndex : positions.size() - 1; + result.add(calculateTripOrStop( + device, positions.get(startEventIndex), positions.get(endIndex), + maxSpeed, ignoreOdometer, reportClass)); + } + } + + return result; + } + + public <T extends BaseReportItem> List<T> fastTripsAndStops( + Device device, Date from, Date to, Class<T> reportClass) throws StorageException { + + List<T> result = new ArrayList<>(); + boolean ignoreOdometer = config.getBoolean(Keys.REPORT_IGNORE_ODOMETER); + boolean trips = reportClass.equals(TripReportItem.class); + Set<String> filter = Set.of(Event.TYPE_DEVICE_MOVING, Event.TYPE_DEVICE_STOPPED); + + var events = storage.getObjects(Event.class, new Request( + new Columns.All(), + new Condition.And( + new Condition.Equals("deviceId", device.getId()), + new Condition.Between("eventTime", "from", from, "to", to)), + new Order("eventTime"))); + var filteredEvents = events.stream() + .filter(event -> filter.contains(event.getType())) + .collect(Collectors.toList()); + + Event startEvent = null; + for (Event event : filteredEvents) { + boolean motion = event.getType().equals(Event.TYPE_DEVICE_MOVING); + if (motion == trips) { + startEvent = event; + } else if (startEvent != null) { + Position startPosition = storage.getObject(Position.class, new Request( + new Columns.All(), new Condition.Equals("id", startEvent.getPositionId()))); + Position endPosition = storage.getObject(Position.class, new Request( + new Columns.All(), new Condition.Equals("id", event.getPositionId()))); + if (startPosition != null && endPosition != null) { + result.add(calculateTripOrStop( + device, startPosition, endPosition, 0, ignoreOdometer, reportClass)); + } + startEvent = null; + } + } + + return result; + } + +} diff --git a/src/main/java/org/traccar/reports/common/TripsConfig.java b/src/main/java/org/traccar/reports/common/TripsConfig.java new file mode 100644 index 000000000..2792114d4 --- /dev/null +++ b/src/main/java/org/traccar/reports/common/TripsConfig.java @@ -0,0 +1,73 @@ +/* + * Copyright 2017 - 2023 Anton Tananaev (anton@traccar.org) + * Copyright 2017 Andrey Kunitsyn (andrey@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.reports.common; + +import org.traccar.config.Keys; +import org.traccar.helper.model.AttributeUtil; + +public class TripsConfig { + + public TripsConfig(AttributeUtil.Provider attributeProvider) { + this( + AttributeUtil.lookup(attributeProvider, Keys.REPORT_TRIP_MINIMAL_TRIP_DISTANCE), + AttributeUtil.lookup(attributeProvider, Keys.REPORT_TRIP_MINIMAL_TRIP_DURATION) * 1000, + AttributeUtil.lookup(attributeProvider, Keys.REPORT_TRIP_MINIMAL_PARKING_DURATION) * 1000, + AttributeUtil.lookup(attributeProvider, Keys.REPORT_TRIP_MINIMAL_NO_DATA_DURATION) * 1000, + AttributeUtil.lookup(attributeProvider, Keys.REPORT_TRIP_USE_IGNITION)); + } + + public TripsConfig( + double minimalTripDistance, long minimalTripDuration, long minimalParkingDuration, + long minimalNoDataDuration, boolean useIgnition) { + this.minimalTripDistance = minimalTripDistance; + this.minimalTripDuration = minimalTripDuration; + this.minimalParkingDuration = minimalParkingDuration; + this.minimalNoDataDuration = minimalNoDataDuration; + this.useIgnition = useIgnition; + } + + private final double minimalTripDistance; + + public double getMinimalTripDistance() { + return minimalTripDistance; + } + + private final long minimalTripDuration; + + public long getMinimalTripDuration() { + return minimalTripDuration; + } + + private final long minimalParkingDuration; + + public long getMinimalParkingDuration() { + return minimalParkingDuration; + } + + private final long minimalNoDataDuration; + + public long getMinimalNoDataDuration() { + return minimalNoDataDuration; + } + + private final boolean useIgnition; + + public boolean getUseIgnition() { + return useIgnition; + } + +} diff --git a/src/main/java/org/traccar/reports/model/BaseReport.java b/src/main/java/org/traccar/reports/model/BaseReportItem.java index 928c0557d..6e270dfe3 100644 --- a/src/main/java/org/traccar/reports/model/BaseReport.java +++ b/src/main/java/org/traccar/reports/model/BaseReportItem.java @@ -18,7 +18,7 @@ package org.traccar.reports.model; import java.util.Date; -public class BaseReport { +public class BaseReportItem { private long deviceId; diff --git a/src/main/java/org/traccar/reports/model/CombinedReportItem.java b/src/main/java/org/traccar/reports/model/CombinedReportItem.java new file mode 100644 index 000000000..810e00916 --- /dev/null +++ b/src/main/java/org/traccar/reports/model/CombinedReportItem.java @@ -0,0 +1,65 @@ +/* + * 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.reports.model; + +import org.traccar.model.Event; +import org.traccar.model.Position; + +import java.util.List; + +public class CombinedReportItem { + + private long deviceId; + + public long getDeviceId() { + return deviceId; + } + + public void setDeviceId(long deviceId) { + this.deviceId = deviceId; + } + + private List<double[]> route; + + public List<double[]> getRoute() { + return route; + } + + public void setRoute(List<double[]> route) { + this.route = route; + } + + private List<Event> events; + + public List<Event> getEvents() { + return events; + } + + public void setEvents(List<Event> events) { + this.events = events; + } + + private List<Position> positions; + + public List<Position> getPositions() { + return positions; + } + + public void setPositions(List<Position> positions) { + this.positions = positions; + } + +} diff --git a/src/main/java/org/traccar/reports/model/DeviceReport.java b/src/main/java/org/traccar/reports/model/DeviceReportSection.java index 932753d15..ffc4d774f 100644 --- a/src/main/java/org/traccar/reports/model/DeviceReport.java +++ b/src/main/java/org/traccar/reports/model/DeviceReportSection.java @@ -20,7 +20,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; -public class DeviceReport { +public class DeviceReportSection { private String deviceName; diff --git a/src/main/java/org/traccar/reports/model/StopReport.java b/src/main/java/org/traccar/reports/model/StopReportItem.java index e20f2c503..3c35bdc21 100644 --- a/src/main/java/org/traccar/reports/model/StopReport.java +++ b/src/main/java/org/traccar/reports/model/StopReportItem.java @@ -16,7 +16,7 @@ */ package org.traccar.reports.model; -public class StopReport extends BaseReport { +public class StopReportItem extends BaseReportItem { private long positionId; diff --git a/src/main/java/org/traccar/reports/model/SummaryReport.java b/src/main/java/org/traccar/reports/model/SummaryReportItem.java index 886f8b9e2..44a15cf1d 100644 --- a/src/main/java/org/traccar/reports/model/SummaryReport.java +++ b/src/main/java/org/traccar/reports/model/SummaryReportItem.java @@ -16,7 +16,7 @@ */ package org.traccar.reports.model; -public class SummaryReport extends BaseReport { +public class SummaryReportItem extends BaseReportItem { private long engineHours; // milliseconds diff --git a/src/main/java/org/traccar/reports/model/TripReport.java b/src/main/java/org/traccar/reports/model/TripReportItem.java index 151c34bd5..332a34cca 100644 --- a/src/main/java/org/traccar/reports/model/TripReport.java +++ b/src/main/java/org/traccar/reports/model/TripReportItem.java @@ -16,7 +16,7 @@ */ package org.traccar.reports.model; -public class TripReport extends BaseReport { +public class TripReportItem extends BaseReportItem { private long startPositionId; diff --git a/src/main/java/org/traccar/reports/model/TripsConfig.java b/src/main/java/org/traccar/reports/model/TripsConfig.java deleted file mode 100644 index 0f0c615d3..000000000 --- a/src/main/java/org/traccar/reports/model/TripsConfig.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2017 Anton Tananaev (anton@traccar.org) - * Copyright 2017 Andrey Kunitsyn (andrey@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.reports.model; - -public class TripsConfig { - - public TripsConfig() { - } - - public TripsConfig(double minimalTripDistance, long minimalTripDuration, long minimalParkingDuration, - long minimalNoDataDuration, boolean useIgnition, boolean processInvalidPositions, double speedThreshold) { - this.minimalTripDistance = minimalTripDistance; - this.minimalTripDuration = minimalTripDuration; - this.minimalParkingDuration = minimalParkingDuration; - this.minimalNoDataDuration = minimalNoDataDuration; - this.useIgnition = useIgnition; - this.processInvalidPositions = processInvalidPositions; - this.speedThreshold = speedThreshold; - } - - private double minimalTripDistance; - - public double getMinimalTripDistance() { - return minimalTripDistance; - } - - public void setMinimalTripDistance(double minimalTripDistance) { - this.minimalTripDistance = minimalTripDistance; - } - - private long minimalTripDuration; - - public long getMinimalTripDuration() { - return minimalTripDuration; - } - - public void setMinimalTripDuration(long minimalTripDuration) { - this.minimalTripDuration = minimalTripDuration; - } - - private long minimalParkingDuration; - - public long getMinimalParkingDuration() { - return minimalParkingDuration; - } - - public void setMinimalParkingDuration(long minimalParkingDuration) { - this.minimalParkingDuration = minimalParkingDuration; - } - - private long minimalNoDataDuration; - - public long getMinimalNoDataDuration() { - return minimalNoDataDuration; - } - - public void setMinimalNoDataDuration(long minimalNoDataDuration) { - this.minimalNoDataDuration = minimalNoDataDuration; - } - - private boolean useIgnition; - - public boolean getUseIgnition() { - return useIgnition; - } - - public void setUseIgnition(boolean useIgnition) { - this.useIgnition = useIgnition; - } - - private boolean processInvalidPositions; - - public boolean getProcessInvalidPositions() { - return processInvalidPositions; - } - - public void setProcessInvalidPositions(boolean processInvalidPositions) { - this.processInvalidPositions = processInvalidPositions; - } - - private double speedThreshold; - - public double getSpeedThreshold() { - return speedThreshold; - } - - public void setSpeedThreshold(double speedThreshold) { - this.speedThreshold = speedThreshold; - } - -} diff --git a/src/main/java/org/traccar/schedule/ScheduleManager.java b/src/main/java/org/traccar/schedule/ScheduleManager.java index 7a3d33b85..07cdb1fe1 100644 --- a/src/main/java/org/traccar/schedule/ScheduleManager.java +++ b/src/main/java/org/traccar/schedule/ScheduleManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2020 - 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. @@ -15,22 +15,35 @@ */ package org.traccar.schedule; +import com.google.inject.Injector; import org.traccar.LifecycleObject; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +@Singleton public class ScheduleManager implements LifecycleObject { + private final Injector injector; private ScheduledExecutorService executor; + @Inject + public ScheduleManager(Injector injector) { + this.injector = injector; + } + @Override public void start() { executor = Executors.newSingleThreadScheduledExecutor(); - - new TaskDeviceInactivityCheck().schedule(executor); - new TaskWebSocketKeepalive().schedule(executor); - new TaskHealthCheck().schedule(executor); + var tasks = List.of( + TaskReports.class, + TaskDeviceInactivityCheck.class, + TaskWebSocketKeepalive.class, + TaskHealthCheck.class); + tasks.forEach(task -> injector.getInstance(task).schedule(executor)); } @Override diff --git a/src/main/java/org/traccar/database/OrderManager.java b/src/main/java/org/traccar/schedule/ScheduleTask.java index c3253e52f..1b537213b 100644 --- a/src/main/java/org/traccar/database/OrderManager.java +++ b/src/main/java/org/traccar/schedule/ScheduleTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,14 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.traccar.database; +package org.traccar.schedule; -import org.traccar.model.Order; - -public class OrderManager extends ExtendedObjectManager<Order> { - - public OrderManager(DataManager dataManager) { - super(dataManager, Order.class); - } +import java.util.concurrent.ScheduledExecutorService; +public interface ScheduleTask extends Runnable { + void schedule(ScheduledExecutorService executor); } diff --git a/src/main/java/org/traccar/schedule/TaskDeviceInactivityCheck.java b/src/main/java/org/traccar/schedule/TaskDeviceInactivityCheck.java index 80641d7d4..8e45568d5 100644 --- a/src/main/java/org/traccar/schedule/TaskDeviceInactivityCheck.java +++ b/src/main/java/org/traccar/schedule/TaskDeviceInactivityCheck.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2020 - 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. @@ -15,17 +15,28 @@ */ package org.traccar.schedule; -import org.traccar.Context; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.database.NotificationManager; import org.traccar.model.Device; import org.traccar.model.Event; +import org.traccar.model.Group; import org.traccar.model.Position; +import org.traccar.storage.Storage; +import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Request; +import jakarta.inject.Inject; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; -public class TaskDeviceInactivityCheck implements Runnable { +public class TaskDeviceInactivityCheck implements ScheduleTask { + + private static final Logger LOGGER = LoggerFactory.getLogger(TaskDeviceInactivityCheck.class); public static final String ATTRIBUTE_DEVICE_INACTIVITY_START = "deviceInactivityStart"; public static final String ATTRIBUTE_DEVICE_INACTIVITY_PERIOD = "deviceInactivityPeriod"; @@ -33,6 +44,16 @@ public class TaskDeviceInactivityCheck implements Runnable { private static final long CHECK_PERIOD_MINUTES = 15; + private final Storage storage; + private final NotificationManager notificationManager; + + @Inject + public TaskDeviceInactivityCheck(Storage storage, NotificationManager notificationManager) { + this.storage = storage; + this.notificationManager = notificationManager; + } + + @Override public void schedule(ScheduledExecutorService executor) { executor.scheduleAtFixedRate(this, CHECK_PERIOD_MINUTES, CHECK_PERIOD_MINUTES, TimeUnit.MINUTES); } @@ -43,19 +64,47 @@ public class TaskDeviceInactivityCheck implements Runnable { long checkPeriod = TimeUnit.MINUTES.toMillis(CHECK_PERIOD_MINUTES); Map<Event, Position> events = new HashMap<>(); - for (Device device : Context.getDeviceManager().getAllDevices()) { - if (device.getLastUpdate() != null && checkDevice(device, currentTime, checkPeriod)) { - Event event = new Event(Event.TYPE_DEVICE_INACTIVE, device.getId()); - event.set(ATTRIBUTE_LAST_UPDATE, device.getLastUpdate().getTime()); - events.put(event, null); + + try { + Map<Long, Group> groups = storage.getObjects(Group.class, new Request(new Columns.All())) + .stream().collect(Collectors.toMap(Group::getId, group -> group)); + for (Device device : storage.getObjects(Device.class, new Request(new Columns.All()))) { + if (device.getLastUpdate() != null && checkDevice(device, groups, currentTime, checkPeriod)) { + Event event = new Event(Event.TYPE_DEVICE_INACTIVE, device.getId()); + event.set(ATTRIBUTE_LAST_UPDATE, device.getLastUpdate().getTime()); + events.put(event, null); + } } + } catch (StorageException e) { + LOGGER.warn("Database error", e); } - Context.getNotificationManager().updateEvents(events); + notificationManager.updateEvents(events); + } + + private long getAttribute(Device device, Map<Long, Group> groups, String key) { + long deviceValue = device.getLong(key); + if (deviceValue > 0) { + return deviceValue; + } else { + long groupId = device.getGroupId(); + while (groupId > 0) { + Group group = groups.get(groupId); + if (group == null) { + return 0; + } + long groupValue = group.getLong(key); + if (groupValue > 0) { + return groupValue; + } + groupId = group.getGroupId(); + } + return 0; + } } - private boolean checkDevice(Device device, long currentTime, long checkPeriod) { - long deviceInactivityStart = device.getLong(ATTRIBUTE_DEVICE_INACTIVITY_START); + private boolean checkDevice(Device device, Map<Long, Group> groups, long currentTime, long checkPeriod) { + long deviceInactivityStart = getAttribute(device, groups, ATTRIBUTE_DEVICE_INACTIVITY_START); if (deviceInactivityStart > 0) { long timeThreshold = device.getLastUpdate().getTime() + deviceInactivityStart; if (currentTime >= timeThreshold) { @@ -64,7 +113,7 @@ public class TaskDeviceInactivityCheck implements Runnable { return true; } - long deviceInactivityPeriod = device.getLong(ATTRIBUTE_DEVICE_INACTIVITY_PERIOD); + long deviceInactivityPeriod = getAttribute(device, groups, ATTRIBUTE_DEVICE_INACTIVITY_PERIOD); if (deviceInactivityPeriod > 0) { long count = (currentTime - timeThreshold - 1) / deviceInactivityPeriod; timeThreshold += count * deviceInactivityPeriod; diff --git a/src/main/java/org/traccar/schedule/TaskHealthCheck.java b/src/main/java/org/traccar/schedule/TaskHealthCheck.java index 087cd3e63..abdc5af48 100644 --- a/src/main/java/org/traccar/schedule/TaskHealthCheck.java +++ b/src/main/java/org/traccar/schedule/TaskHealthCheck.java @@ -19,23 +19,31 @@ import com.sun.jna.Library; import com.sun.jna.Native; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.traccar.Context; +import org.traccar.config.Config; import org.traccar.config.Keys; +import jakarta.inject.Inject; +import jakarta.ws.rs.client.Client; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -public class TaskHealthCheck implements Runnable { +public class TaskHealthCheck implements ScheduleTask { private static final Logger LOGGER = LoggerFactory.getLogger(TaskHealthCheck.class); + private final Config config; + private final Client client; + private SystemD systemD; private boolean enabled; private long period; - public TaskHealthCheck() { - if (!Context.getConfig().getBoolean(Keys.WEB_DISABLE_HEALTH_CHECK) + @Inject + public TaskHealthCheck(Config config, Client client) { + this.config = config; + this.client = client; + if (!config.getBoolean(Keys.WEB_DISABLE_HEALTH_CHECK) && System.getProperty("os.name").toLowerCase().startsWith("linux")) { try { systemD = Native.load("systemd", SystemD.class); @@ -54,11 +62,12 @@ public class TaskHealthCheck implements Runnable { } private String getUrl() { - String address = Context.getConfig().getString(Keys.WEB_ADDRESS, "localhost"); - int port = Context.getConfig().getInteger(Keys.WEB_PORT); + String address = config.getString(Keys.WEB_ADDRESS, "localhost"); + int port = config.getInteger(Keys.WEB_PORT); return "http://" + address + ":" + port + "/api/server"; } + @Override public void schedule(ScheduledExecutorService executor) { if (enabled) { executor.scheduleAtFixedRate(this, period, period, TimeUnit.MILLISECONDS); @@ -68,7 +77,7 @@ public class TaskHealthCheck implements Runnable { @Override public void run() { LOGGER.debug("Health check running"); - int status = Context.getClient().target(getUrl()).request().get().getStatus(); + int status = client.target(getUrl()).request().get().getStatus(); if (status == 200) { int result = systemD.sd_notify(0, "WATCHDOG=1"); if (result < 0) { diff --git a/src/main/java/org/traccar/schedule/TaskReports.java b/src/main/java/org/traccar/schedule/TaskReports.java new file mode 100644 index 000000000..30f20f437 --- /dev/null +++ b/src/main/java/org/traccar/schedule/TaskReports.java @@ -0,0 +1,146 @@ +/* + * 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.schedule; + +import com.google.inject.Injector; +import com.google.inject.servlet.RequestScoper; +import com.google.inject.servlet.ServletScopes; +import net.fortuna.ical4j.model.Period; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.model.BaseModel; +import org.traccar.model.Calendar; +import org.traccar.model.Device; +import org.traccar.model.Group; +import org.traccar.model.Report; +import org.traccar.model.User; +import org.traccar.reports.EventsReportProvider; +import org.traccar.reports.RouteReportProvider; +import org.traccar.reports.StopsReportProvider; +import org.traccar.reports.SummaryReportProvider; +import org.traccar.reports.TripsReportProvider; +import org.traccar.reports.common.ReportMailer; +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 java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +public class TaskReports implements ScheduleTask { + + private static final Logger LOGGER = LoggerFactory.getLogger(TaskReports.class); + + private static final long CHECK_PERIOD_MINUTES = 1; + + private final Storage storage; + private final Injector injector; + + @Inject + public TaskReports(Storage storage, Injector injector) { + this.storage = storage; + this.injector = injector; + } + + @Override + public void schedule(ScheduledExecutorService executor) { + executor.scheduleAtFixedRate(this, CHECK_PERIOD_MINUTES, CHECK_PERIOD_MINUTES, TimeUnit.MINUTES); + } + + @Override + public void run() { + Date currentCheck = new Date(); + Date lastCheck = new Date(System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(CHECK_PERIOD_MINUTES)); + + try { + for (Report report : storage.getObjects(Report.class, new Request(new Columns.All()))) { + Calendar calendar = storage.getObject(Calendar.class, new Request( + new Columns.All(), new Condition.Equals("id", report.getCalendarId()))); + + var lastEvents = calendar.findPeriods(lastCheck); + var currentEvents = calendar.findPeriods(currentCheck); + + if (!lastEvents.isEmpty() && currentEvents.isEmpty()) { + Period period = lastEvents.iterator().next(); + RequestScoper scope = ServletScopes.scopeRequest(Collections.emptyMap()); + try (RequestScoper.CloseableScope ignored = scope.open()) { + executeReport(report, period.getStart(), period.getEnd()); + } + } + } + } catch (StorageException e) { + LOGGER.warn("Scheduled reports error", e); + } + } + + private void executeReport(Report report, Date from, Date to) throws StorageException { + + var deviceIds = storage.getObjects(Device.class, new Request( + new Columns.Include("id"), + new Condition.Permission(Device.class, Report.class, report.getId()))) + .stream().map(BaseModel::getId).collect(Collectors.toList()); + var groupIds = storage.getObjects(Group.class, new Request( + new Columns.Include("id"), + new Condition.Permission(Group.class, Report.class, report.getId()))) + .stream().map(BaseModel::getId).collect(Collectors.toList()); + var users = storage.getObjects(User.class, new Request( + new Columns.Include("id"), + new Condition.Permission(User.class, Report.class, report.getId()))); + + ReportMailer reportMailer = injector.getInstance(ReportMailer.class); + + for (User user : users) { + switch (report.getType()) { + case "events": + var eventsReportProvider = injector.getInstance(EventsReportProvider.class); + reportMailer.sendAsync(user.getId(), stream -> eventsReportProvider.getExcel( + stream, user.getId(), deviceIds, groupIds, List.of(), from, to)); + break; + case "route": + var routeReportProvider = injector.getInstance(RouteReportProvider.class); + reportMailer.sendAsync(user.getId(), stream -> routeReportProvider.getExcel( + stream, user.getId(), deviceIds, groupIds, from, to)); + break; + case "summary": + var summaryReportProvider = injector.getInstance(SummaryReportProvider.class); + reportMailer.sendAsync(user.getId(), stream -> summaryReportProvider.getExcel( + stream, user.getId(), deviceIds, groupIds, from, to, false)); + break; + case "trips": + var tripsReportProvider = injector.getInstance(TripsReportProvider.class); + reportMailer.sendAsync(user.getId(), stream -> tripsReportProvider.getExcel( + stream, user.getId(), deviceIds, groupIds, from, to)); + break; + case "stops": + var stopsReportProvider = injector.getInstance(StopsReportProvider.class); + reportMailer.sendAsync(user.getId(), stream -> stopsReportProvider.getExcel( + stream, user.getId(), deviceIds, groupIds, from, to)); + break; + default: + LOGGER.warn("Unsupported report type {}", report.getType()); + break; + } + } + } + +} diff --git a/src/main/java/org/traccar/schedule/TaskWebSocketKeepalive.java b/src/main/java/org/traccar/schedule/TaskWebSocketKeepalive.java index 953b0efea..d9e0c6f0b 100644 --- a/src/main/java/org/traccar/schedule/TaskWebSocketKeepalive.java +++ b/src/main/java/org/traccar/schedule/TaskWebSocketKeepalive.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,22 +15,31 @@ */ package org.traccar.schedule; -import org.traccar.Context; +import org.traccar.session.ConnectionManager; +import jakarta.inject.Inject; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -public class TaskWebSocketKeepalive implements Runnable { +public class TaskWebSocketKeepalive implements ScheduleTask { private static final long PERIOD_SECONDS = 55; + private final ConnectionManager connectionManager; + + @Inject + public TaskWebSocketKeepalive(ConnectionManager connectionManager) { + this.connectionManager = connectionManager; + } + + @Override public void schedule(ScheduledExecutorService executor) { executor.scheduleAtFixedRate(this, PERIOD_SECONDS, PERIOD_SECONDS, TimeUnit.SECONDS); } @Override public void run() { - Context.getConnectionManager().sendKeepalive(); + connectionManager.sendKeepalive(); } } diff --git a/src/main/java/org/traccar/session/ConnectionManager.java b/src/main/java/org/traccar/session/ConnectionManager.java new file mode 100644 index 000000000..28214840d --- /dev/null +++ b/src/main/java/org/traccar/session/ConnectionManager.java @@ -0,0 +1,376 @@ +/* + * 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.session; + +import io.netty.channel.Channel; +import io.netty.util.Timeout; +import io.netty.util.Timer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.Protocol; +import org.traccar.broadcast.BroadcastInterface; +import org.traccar.broadcast.BroadcastService; +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.database.DeviceLookupService; +import org.traccar.database.NotificationManager; +import org.traccar.model.BaseModel; +import org.traccar.model.Device; +import org.traccar.model.Event; +import org.traccar.model.Position; +import org.traccar.model.User; +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.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +@Singleton +public class ConnectionManager implements BroadcastInterface { + + private static final Logger LOGGER = LoggerFactory.getLogger(ConnectionManager.class); + + private final long deviceTimeout; + + private final Map<Long, DeviceSession> sessionsByDeviceId = new ConcurrentHashMap<>(); + private final Map<Endpoint, Map<String, DeviceSession>> sessionsByEndpoint = new ConcurrentHashMap<>(); + + private final Config config; + private final CacheManager cacheManager; + private final Storage storage; + private final NotificationManager notificationManager; + private final Timer timer; + private final BroadcastService broadcastService; + private final DeviceLookupService deviceLookupService; + + private final Map<Long, Set<UpdateListener>> listeners = new HashMap<>(); + private final Map<Long, Set<Long>> userDevices = new HashMap<>(); + private final Map<Long, Set<Long>> deviceUsers = new HashMap<>(); + + private final Map<Long, Timeout> timeouts = new ConcurrentHashMap<>(); + + @Inject + public ConnectionManager( + Config config, CacheManager cacheManager, Storage storage, + NotificationManager notificationManager, Timer timer, BroadcastService broadcastService, + DeviceLookupService deviceLookupService) { + this.config = config; + this.cacheManager = cacheManager; + this.storage = storage; + this.notificationManager = notificationManager; + this.timer = timer; + this.broadcastService = broadcastService; + this.deviceLookupService = deviceLookupService; + deviceTimeout = config.getLong(Keys.STATUS_TIMEOUT); + broadcastService.registerListener(this); + } + + public DeviceSession getDeviceSession(long deviceId) { + return sessionsByDeviceId.get(deviceId); + } + + public DeviceSession getDeviceSession( + Protocol protocol, Channel channel, SocketAddress remoteAddress, + String... uniqueIds) throws StorageException { + + Endpoint endpoint = new Endpoint(channel, remoteAddress); + Map<String, DeviceSession> endpointSessions = sessionsByEndpoint.getOrDefault( + endpoint, new ConcurrentHashMap<>()); + + uniqueIds = Arrays.stream(uniqueIds).filter(Objects::nonNull).toArray(String[]::new); + if (uniqueIds.length > 0) { + for (String uniqueId : uniqueIds) { + DeviceSession deviceSession = endpointSessions.get(uniqueId); + if (deviceSession != null) { + return deviceSession; + } + } + } else { + return endpointSessions.values().stream().findAny().orElse(null); + } + + Device device = deviceLookupService.lookup(uniqueIds); + + if (device == null && config.getBoolean(Keys.DATABASE_REGISTER_UNKNOWN)) { + if (uniqueIds[0].matches(config.getString(Keys.DATABASE_REGISTER_UNKNOWN_REGEX))) { + device = addUnknownDevice(uniqueIds[0]); + } + } + + if (device != null) { + device.checkDisabled(); + + DeviceSession oldSession = sessionsByDeviceId.remove(device.getId()); + if (oldSession != null) { + Endpoint oldEndpoint = new Endpoint(oldSession.getChannel(), oldSession.getRemoteAddress()); + Map<String, DeviceSession> oldEndpointSessions = sessionsByEndpoint.get(oldEndpoint); + if (oldEndpointSessions != null && oldEndpointSessions.size() > 1) { + oldEndpointSessions.remove(device.getUniqueId()); + } else { + sessionsByEndpoint.remove(oldEndpoint); + } + } + + DeviceSession deviceSession = new DeviceSession( + device.getId(), device.getUniqueId(), protocol, channel, remoteAddress); + endpointSessions.put(device.getUniqueId(), deviceSession); + sessionsByEndpoint.put(endpoint, endpointSessions); + sessionsByDeviceId.put(device.getId(), deviceSession); + + if (oldSession == null) { + cacheManager.addDevice(device.getId()); + } + + return deviceSession; + } else { + LOGGER.warn("Unknown device - " + String.join(" ", uniqueIds) + + " (" + ((InetSocketAddress) remoteAddress).getHostString() + ")"); + return null; + } + } + + private Device addUnknownDevice(String uniqueId) { + Device device = new Device(); + device.setName(uniqueId); + device.setUniqueId(uniqueId); + device.setCategory(config.getString(Keys.DATABASE_REGISTER_UNKNOWN_DEFAULT_CATEGORY)); + + long defaultGroupId = config.getLong(Keys.DATABASE_REGISTER_UNKNOWN_DEFAULT_GROUP_ID); + if (defaultGroupId != 0) { + device.setGroupId(defaultGroupId); + } + + try { + device.setId(storage.addObject(device, new Request(new Columns.Exclude("id")))); + LOGGER.info("Automatically registered " + uniqueId); + return device; + } catch (StorageException e) { + LOGGER.warn("Automatic registration failed", e); + return null; + } + } + + public void deviceDisconnected(Channel channel, boolean supportsOffline) { + Endpoint endpoint = new Endpoint(channel, channel.remoteAddress()); + Map<String, DeviceSession> endpointSessions = sessionsByEndpoint.remove(endpoint); + if (endpointSessions != null) { + for (DeviceSession deviceSession : endpointSessions.values()) { + if (supportsOffline) { + updateDevice(deviceSession.getDeviceId(), Device.STATUS_OFFLINE, null); + } + sessionsByDeviceId.remove(deviceSession.getDeviceId()); + cacheManager.removeDevice(deviceSession.getDeviceId()); + } + } + } + + public void deviceUnknown(long deviceId) { + updateDevice(deviceId, Device.STATUS_UNKNOWN, null); + removeDeviceSession(deviceId); + } + + private void removeDeviceSession(long deviceId) { + DeviceSession deviceSession = sessionsByDeviceId.remove(deviceId); + if (deviceSession != null) { + cacheManager.removeDevice(deviceId); + Endpoint endpoint = new Endpoint(deviceSession.getChannel(), deviceSession.getRemoteAddress()); + sessionsByEndpoint.computeIfPresent(endpoint, (e, sessions) -> { + sessions.remove(deviceSession.getUniqueId()); + return sessions.isEmpty() ? null : sessions; + }); + } + } + + public void updateDevice(long deviceId, String status, Date time) { + Device device = cacheManager.getObject(Device.class, deviceId); + if (device == null) { + try { + device = storage.getObject(Device.class, new Request( + new Columns.All(), new Condition.Equals("id", deviceId))); + } catch (StorageException e) { + LOGGER.warn("Failed to get device", e); + } + if (device == null) { + return; + } + } + + String oldStatus = device.getStatus(); + device.setStatus(status); + + if (!status.equals(oldStatus)) { + String eventType; + Map<Event, Position> events = new HashMap<>(); + switch (status) { + case Device.STATUS_ONLINE: + eventType = Event.TYPE_DEVICE_ONLINE; + break; + case Device.STATUS_UNKNOWN: + eventType = Event.TYPE_DEVICE_UNKNOWN; + break; + default: + eventType = Event.TYPE_DEVICE_OFFLINE; + break; + } + events.put(new Event(eventType, deviceId), null); + notificationManager.updateEvents(events); + } + + if (time != null) { + device.setLastUpdate(time); + } + + Timeout timeout = timeouts.remove(deviceId); + if (timeout != null) { + timeout.cancel(); + } + + if (status.equals(Device.STATUS_ONLINE)) { + timeouts.put(deviceId, timer.newTimeout(timeout1 -> { + if (!timeout1.isCancelled()) { + deviceUnknown(deviceId); + } + }, deviceTimeout, TimeUnit.SECONDS)); + } + + try { + storage.updateObject(device, new Request( + new Columns.Include("status", "lastUpdate"), + new Condition.Equals("id", deviceId))); + } catch (StorageException e) { + LOGGER.warn("Update device status error", e); + } + + updateDevice(true, device); + } + + public synchronized void sendKeepalive() { + for (Set<UpdateListener> userListeners : listeners.values()) { + for (UpdateListener listener : userListeners) { + listener.onKeepalive(); + } + } + } + + @Override + public synchronized void updateDevice(boolean local, Device device) { + if (local) { + broadcastService.updateDevice(true, device); + } else if (Device.STATUS_ONLINE.equals(device.getStatus())) { + timeouts.remove(device.getId()); + removeDeviceSession(device.getId()); + } + for (long userId : deviceUsers.getOrDefault(device.getId(), Collections.emptySet())) { + if (listeners.containsKey(userId)) { + for (UpdateListener listener : listeners.get(userId)) { + listener.onUpdateDevice(device); + } + } + } + } + + @Override + public synchronized void updatePosition(boolean local, Position position) { + if (local) { + broadcastService.updatePosition(true, position); + } + for (long userId : deviceUsers.getOrDefault(position.getDeviceId(), Collections.emptySet())) { + if (listeners.containsKey(userId)) { + for (UpdateListener listener : listeners.get(userId)) { + listener.onUpdatePosition(position); + } + } + } + } + + @Override + public synchronized void updateEvent(boolean local, long userId, Event event) { + if (local) { + broadcastService.updateEvent(true, userId, event); + } + if (listeners.containsKey(userId)) { + for (UpdateListener listener : listeners.get(userId)) { + listener.onUpdateEvent(event); + } + } + } + + @Override + public synchronized void invalidatePermission( + boolean local, + Class<? extends BaseModel> clazz1, long id1, + Class<? extends BaseModel> clazz2, long id2) { + if (clazz1.equals(User.class) && clazz2.equals(Device.class)) { + if (listeners.containsKey(id1)) { + userDevices.get(id1).add(id2); + deviceUsers.put(id2, new HashSet<>(List.of(id1))); + } + } + } + + public interface UpdateListener { + void onKeepalive(); + void onUpdateDevice(Device device); + void onUpdatePosition(Position position); + void onUpdateEvent(Event event); + } + + public synchronized void addListener(long userId, UpdateListener listener) throws StorageException { + var set = listeners.get(userId); + if (set == null) { + set = new HashSet<>(); + listeners.put(userId, set); + + var devices = storage.getObjects(Device.class, new Request( + new Columns.Include("id"), new Condition.Permission(User.class, userId, Device.class))); + userDevices.put(userId, devices.stream().map(BaseModel::getId).collect(Collectors.toSet())); + devices.forEach(device -> deviceUsers.computeIfAbsent(device.getId(), id -> new HashSet<>()).add(userId)); + } + set.add(listener); + } + + public synchronized void removeListener(long userId, UpdateListener listener) { + var set = listeners.get(userId); + set.remove(listener); + if (set.isEmpty()) { + listeners.remove(userId); + + userDevices.remove(userId).forEach(deviceId -> deviceUsers.computeIfPresent(deviceId, (x, userIds) -> { + userIds.remove(userId); + return userIds.isEmpty() ? null : userIds; + })); + } + } + +} diff --git a/src/main/java/org/traccar/database/ActiveDevice.java b/src/main/java/org/traccar/session/DeviceSession.java index c05d56ad2..009f90f5a 100644 --- a/src/main/java/org/traccar/database/ActiveDevice.java +++ b/src/main/java/org/traccar/session/DeviceSession.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.traccar.database; +package org.traccar.session; import io.netty.channel.Channel; import io.netty.handler.codec.http.HttpRequestDecoder; @@ -22,37 +22,69 @@ import org.traccar.Protocol; import org.traccar.model.Command; import java.net.SocketAddress; +import java.util.HashMap; +import java.util.Map; -public class ActiveDevice { +public class DeviceSession { private final long deviceId; + private final String uniqueId; private final Protocol protocol; private final Channel channel; private final SocketAddress remoteAddress; - private final boolean supportsLiveCommands; - public ActiveDevice(long deviceId, Protocol protocol, Channel channel, SocketAddress remoteAddress) { + public DeviceSession( + long deviceId, String uniqueId, Protocol protocol, Channel channel, SocketAddress remoteAddress) { this.deviceId = deviceId; + this.uniqueId = uniqueId; this.protocol = protocol; this.channel = channel; this.remoteAddress = remoteAddress; - supportsLiveCommands = BasePipelineFactory.getHandler(channel.pipeline(), HttpRequestDecoder.class) == null; + } + + public long getDeviceId() { + return deviceId; + } + + public String getUniqueId() { + return uniqueId; } public Channel getChannel() { return channel; } - public long getDeviceId() { - return deviceId; + public SocketAddress getRemoteAddress() { + return remoteAddress; } public boolean supportsLiveCommands() { - return supportsLiveCommands; + return BasePipelineFactory.getHandler(channel.pipeline(), HttpRequestDecoder.class) == null; } public void sendCommand(Command command) { protocol.sendDataCommand(channel, remoteAddress, command); } + public static final String KEY_TIMEZONE = "timezone"; + + private final Map<String, Object> locals = new HashMap<>(); + + public boolean contains(String key) { + return locals.containsKey(key); + } + + public void set(String key, Object value) { + if (value != null) { + locals.put(key, value); + } else { + locals.remove(key); + } + } + + @SuppressWarnings("unchecked") + public <T> T get(String key) { + return (T) locals.get(key); + } + } diff --git a/src/main/java/org/traccar/session/Endpoint.java b/src/main/java/org/traccar/session/Endpoint.java new file mode 100644 index 000000000..76aac3444 --- /dev/null +++ b/src/main/java/org/traccar/session/Endpoint.java @@ -0,0 +1,58 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.session; + +import io.netty.channel.Channel; + +import java.net.SocketAddress; +import java.util.Objects; + +public class Endpoint { + + private final Channel channel; + private final SocketAddress remoteAddress; + + public Endpoint(Channel channel, SocketAddress remoteAddress) { + this.channel = channel; + this.remoteAddress = remoteAddress; + } + + public Channel getChannel() { + return channel; + } + + public SocketAddress getRemoteAddress() { + return remoteAddress; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Endpoint endpoint = (Endpoint) o; + return channel.equals(endpoint.channel) && remoteAddress.equals(endpoint.remoteAddress); + } + + @Override + public int hashCode() { + return Objects.hash(channel, remoteAddress); + } + +} diff --git a/src/main/java/org/traccar/session/cache/CacheKey.java b/src/main/java/org/traccar/session/cache/CacheKey.java new file mode 100644 index 000000000..23145e34b --- /dev/null +++ b/src/main/java/org/traccar/session/cache/CacheKey.java @@ -0,0 +1,57 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.session.cache; + +import org.traccar.model.BaseModel; + +import java.util.Objects; + +class CacheKey { + + private final Class<? extends BaseModel> clazz; + private final long id; + + CacheKey(BaseModel object) { + this(object.getClass(), object.getId()); + } + + CacheKey(Class<? extends BaseModel> clazz, long id) { + this.clazz = clazz; + this.id = id; + } + + public boolean classIs(Class<? extends BaseModel> clazz) { + return clazz.equals(this.clazz); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CacheKey cacheKey = (CacheKey) o; + return id == cacheKey.id && Objects.equals(clazz, cacheKey.clazz); + } + + @Override + public int hashCode() { + return Objects.hash(clazz, id); + } + +} diff --git a/src/main/java/org/traccar/session/cache/CacheManager.java b/src/main/java/org/traccar/session/cache/CacheManager.java new file mode 100644 index 000000000..58320cf29 --- /dev/null +++ b/src/main/java/org/traccar/session/cache/CacheManager.java @@ -0,0 +1,428 @@ +/* + * 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.session.cache; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.broadcast.BroadcastInterface; +import org.traccar.broadcast.BroadcastService; +import org.traccar.config.Config; +import org.traccar.model.Attribute; +import org.traccar.model.BaseModel; +import org.traccar.model.Calendar; +import org.traccar.model.Device; +import org.traccar.model.Driver; +import org.traccar.model.Geofence; +import org.traccar.model.Group; +import org.traccar.model.GroupedModel; +import org.traccar.model.Maintenance; +import org.traccar.model.Notification; +import org.traccar.model.Position; +import org.traccar.model.Schedulable; +import org.traccar.model.Server; +import org.traccar.model.User; +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.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.stream.Collectors; + +@Singleton +public class CacheManager implements BroadcastInterface { + + private static final Logger LOGGER = LoggerFactory.getLogger(CacheManager.class); + private static final int GROUP_DEPTH_LIMIT = 3; + private static final Collection<Class<? extends BaseModel>> CLASSES = Arrays.asList( + Attribute.class, Driver.class, Geofence.class, Maintenance.class, Notification.class); + + private final Config config; + private final Storage storage; + private final BroadcastService broadcastService; + + private final ReadWriteLock lock = new ReentrantReadWriteLock(); + + private final Map<CacheKey, CacheValue> deviceCache = new HashMap<>(); + private final Map<Long, Integer> deviceReferences = new HashMap<>(); + private final Map<Long, Map<Class<? extends BaseModel>, Set<Long>>> deviceLinks = new HashMap<>(); + private final Map<Long, Position> devicePositions = new HashMap<>(); + + private Server server; + private final Map<Long, List<User>> notificationUsers = new HashMap<>(); + + @Inject + public CacheManager(Config config, Storage storage, BroadcastService broadcastService) throws StorageException { + this.config = config; + this.storage = storage; + this.broadcastService = broadcastService; + invalidateServer(); + invalidateUsers(); + broadcastService.registerListener(this); + } + + public Config getConfig() { + return config; + } + + public <T extends BaseModel> T getObject(Class<T> clazz, long id) { + try { + lock.readLock().lock(); + var cacheValue = deviceCache.get(new CacheKey(clazz, id)); + return cacheValue != null ? cacheValue.getValue() : null; + } finally { + lock.readLock().unlock(); + } + } + + public <T extends BaseModel> List<T> getDeviceObjects(long deviceId, Class<T> clazz) { + try { + lock.readLock().lock(); + var links = deviceLinks.get(deviceId); + if (links != null) { + return links.getOrDefault(clazz, new LinkedHashSet<>()).stream() + .map(id -> { + var cacheValue = deviceCache.get(new CacheKey(clazz, id)); + return cacheValue != null ? cacheValue.<T>getValue() : null; + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } else { + LOGGER.warn("Device {} cache missing", deviceId); + return Collections.emptyList(); + } + } finally { + lock.readLock().unlock(); + } + } + + public Position getPosition(long deviceId) { + try { + lock.readLock().lock(); + return devicePositions.get(deviceId); + } finally { + lock.readLock().unlock(); + } + } + + public Server getServer() { + try { + lock.readLock().lock(); + return server; + } finally { + lock.readLock().unlock(); + } + } + + public List<User> getNotificationUsers(long notificationId, long deviceId) { + try { + lock.readLock().lock(); + var users = deviceLinks.get(deviceId).get(User.class).stream() + .collect(Collectors.toUnmodifiableSet()); + return notificationUsers.getOrDefault(notificationId, new LinkedList<>()).stream() + .filter(user -> users.contains(user.getId())) + .collect(Collectors.toUnmodifiableList()); + } finally { + lock.readLock().unlock(); + } + } + + public Driver findDriverByUniqueId(long deviceId, String driverUniqueId) { + return getDeviceObjects(deviceId, Driver.class).stream() + .filter(driver -> driver.getUniqueId().equals(driverUniqueId)) + .findFirst() + .orElse(null); + } + + public void addDevice(long deviceId) throws StorageException { + try { + lock.writeLock().lock(); + Integer references = deviceReferences.get(deviceId); + if (references != null) { + references += 1; + } else { + unsafeAddDevice(deviceId); + references = 1; + } + deviceReferences.put(deviceId, references); + } finally { + lock.writeLock().unlock(); + } + } + + public void removeDevice(long deviceId) { + try { + lock.writeLock().lock(); + Integer references = deviceReferences.get(deviceId); + if (references != null) { + references -= 1; + if (references <= 0) { + unsafeRemoveDevice(deviceId); + deviceReferences.remove(deviceId); + } else { + deviceReferences.put(deviceId, references); + } + } + } finally { + lock.writeLock().unlock(); + } + } + + public void updatePosition(Position position) { + try { + lock.writeLock().lock(); + if (deviceLinks.containsKey(position.getDeviceId())) { + devicePositions.put(position.getDeviceId(), position); + } + } finally { + lock.writeLock().unlock(); + } + } + + @Override + public void invalidateObject(boolean local, Class<? extends BaseModel> clazz, long id) { + try { + var object = storage.getObject(clazz, new Request( + new Columns.All(), new Condition.Equals("id", id))); + if (object != null) { + updateOrInvalidate(local, object); + } else { + invalidate(clazz, id); + } + } catch (StorageException e) { + throw new RuntimeException(e); + } + } + + public <T extends BaseModel> void updateOrInvalidate(boolean local, T object) throws StorageException { + if (local) { + broadcastService.invalidateObject(true, object.getClass(), object.getId()); + } + + if (object instanceof Server) { + invalidateServer(); + return; + } + if (object instanceof User) { + invalidateUsers(); + return; + } + + boolean invalidate = false; + var before = getObject(object.getClass(), object.getId()); + if (before == null) { + return; + } else if (object instanceof GroupedModel) { + if (((GroupedModel) before).getGroupId() != ((GroupedModel) object).getGroupId()) { + invalidate = true; + } + } else if (object instanceof Schedulable) { + if (((Schedulable) before).getCalendarId() != ((Schedulable) object).getCalendarId()) { + invalidate = true; + } + } + if (invalidate) { + invalidate(object.getClass(), object.getId()); + } else { + try { + lock.writeLock().lock(); + deviceCache.get(new CacheKey(object.getClass(), object.getId())).setValue(object); + } finally { + lock.writeLock().unlock(); + } + } + } + + public <T extends BaseModel> void invalidate(Class<T> clazz, long id) throws StorageException { + invalidate(new CacheKey(clazz, id)); + } + + @Override + public void invalidatePermission( + boolean local, + Class<? extends BaseModel> clazz1, long id1, + Class<? extends BaseModel> clazz2, long id2) { + if (local) { + broadcastService.invalidatePermission(true, clazz1, id1, clazz2, id2); + } + + try { + invalidate(new CacheKey(clazz1, id1), new CacheKey(clazz2, id2)); + } catch (StorageException e) { + throw new RuntimeException(e); + } + } + + private void invalidateServer() throws StorageException { + server = storage.getObject(Server.class, new Request(new Columns.All())); + } + + private void invalidateUsers() throws StorageException { + notificationUsers.clear(); + Map<Long, User> users = new HashMap<>(); + storage.getObjects(User.class, new Request(new Columns.All())) + .forEach(user -> users.put(user.getId(), user)); + storage.getPermissions(User.class, Notification.class).forEach(permission -> { + long notificationId = permission.getPropertyId(); + var user = users.get(permission.getOwnerId()); + notificationUsers.computeIfAbsent(notificationId, k -> new LinkedList<>()).add(user); + }); + } + + private void addObject(long deviceId, BaseModel object) { + deviceCache.computeIfAbsent(new CacheKey(object), k -> new CacheValue(object)).retain(deviceId); + } + + private void unsafeAddDevice(long deviceId) throws StorageException { + Map<Class<? extends BaseModel>, Set<Long>> links = new HashMap<>(); + + Device device = storage.getObject(Device.class, new Request( + new Columns.All(), new Condition.Equals("id", deviceId))); + if (device != null) { + addObject(deviceId, device); + if (device.getCalendarId() > 0) { + var calendar = storage.getObject(Calendar.class, new Request( + new Columns.All(), new Condition.Equals("id", device.getCalendarId()))); + links.computeIfAbsent(Calendar.class, k -> new LinkedHashSet<>()).add(calendar.getId()); + addObject(deviceId, calendar); + } + + int groupDepth = 0; + long groupId = device.getGroupId(); + while (groupDepth < GROUP_DEPTH_LIMIT && groupId > 0) { + Group group = storage.getObject(Group.class, new Request( + new Columns.All(), new Condition.Equals("id", groupId))); + links.computeIfAbsent(Group.class, k -> new LinkedHashSet<>()).add(group.getId()); + addObject(deviceId, group); + groupId = group.getGroupId(); + groupDepth += 1; + } + + for (Class<? extends BaseModel> clazz : CLASSES) { + var objects = storage.getObjects(clazz, new Request( + new Columns.All(), new Condition.Permission(Device.class, deviceId, clazz))); + links.put(clazz, objects.stream().map(BaseModel::getId).collect(Collectors.toSet())); + for (var object : objects) { + addObject(deviceId, object); + if (object instanceof Schedulable) { + var scheduled = (Schedulable) object; + if (scheduled.getCalendarId() > 0) { + var calendar = storage.getObject(Calendar.class, new Request( + new Columns.All(), new Condition.Equals("id", scheduled.getCalendarId()))); + links.computeIfAbsent(Calendar.class, k -> new LinkedHashSet<>()).add(calendar.getId()); + addObject(deviceId, calendar); + } + } + } + } + + var users = storage.getObjects(User.class, new Request( + new Columns.All(), new Condition.Permission(User.class, Device.class, deviceId))); + links.put(User.class, users.stream().map(BaseModel::getId).collect(Collectors.toSet())); + for (var user : users) { + addObject(deviceId, user); + var notifications = storage.getObjects(Notification.class, new Request( + new Columns.All(), + new Condition.Permission(User.class, user.getId(), Notification.class))).stream() + .filter(Notification::getAlways) + .collect(Collectors.toList()); + for (var notification : notifications) { + links.computeIfAbsent(Notification.class, k -> new LinkedHashSet<>()).add(notification.getId()); + addObject(deviceId, notification); + if (notification.getCalendarId() > 0) { + var calendar = storage.getObject(Calendar.class, new Request( + new Columns.All(), new Condition.Equals("id", notification.getCalendarId()))); + links.computeIfAbsent(Calendar.class, k -> new LinkedHashSet<>()).add(calendar.getId()); + addObject(deviceId, calendar); + } + } + } + + deviceLinks.put(deviceId, links); + + if (device.getPositionId() > 0) { + devicePositions.put(deviceId, storage.getObject(Position.class, new Request( + new Columns.All(), new Condition.Equals("id", device.getPositionId())))); + } + } + } + + private void unsafeRemoveDevice(long deviceId) { + deviceCache.remove(new CacheKey(Device.class, deviceId)); + deviceLinks.remove(deviceId).forEach((clazz, ids) -> ids.forEach(id -> { + var key = new CacheKey(clazz, id); + deviceCache.computeIfPresent(key, (k, value) -> { + value.release(deviceId); + return value.getReferences().size() > 0 ? value : null; + }); + })); + devicePositions.remove(deviceId); + } + + private void invalidate(CacheKey... keys) throws StorageException { + try { + lock.writeLock().lock(); + unsafeInvalidate(keys); + } finally { + lock.writeLock().unlock(); + } + } + + private void unsafeInvalidate(CacheKey[] keys) throws StorageException { + boolean invalidateServer = false; + boolean invalidateUsers = false; + Set<Long> linkedDevices = new HashSet<>(); + for (var key : keys) { + if (key.classIs(Server.class)) { + invalidateServer = true; + } else { + if (key.classIs(User.class) || key.classIs(Notification.class)) { + invalidateUsers = true; + } + deviceCache.computeIfPresent(key, (k, value) -> { + linkedDevices.addAll(value.getReferences()); + return value; + }); + } + } + for (long deviceId : linkedDevices) { + unsafeRemoveDevice(deviceId); + unsafeAddDevice(deviceId); + } + if (invalidateServer) { + invalidateServer(); + } + if (invalidateUsers) { + invalidateUsers(); + } + } + +} diff --git a/src/main/java/org/traccar/session/cache/CacheValue.java b/src/main/java/org/traccar/session/cache/CacheValue.java new file mode 100644 index 000000000..1f0383ce5 --- /dev/null +++ b/src/main/java/org/traccar/session/cache/CacheValue.java @@ -0,0 +1,53 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.session.cache; + +import org.traccar.model.BaseModel; + +import java.util.HashSet; +import java.util.Set; + +class CacheValue { + + private BaseModel value; + private final Set<Long> references = new HashSet<>(); + + CacheValue(BaseModel value) { + this.value = value; + } + + public void retain(long deviceId) { + references.add(deviceId); + } + + public void release(long deviceId) { + references.remove(deviceId); + } + + @SuppressWarnings("unchecked") + public <T extends BaseModel> T getValue() { + return (T) value; + } + + public void setValue(BaseModel value) { + this.value = value; + } + + public Set<Long> getReferences() { + return references; + } + +} diff --git a/src/main/java/org/traccar/session/state/MotionProcessor.java b/src/main/java/org/traccar/session/state/MotionProcessor.java new file mode 100644 index 000000000..a1737a739 --- /dev/null +++ b/src/main/java/org/traccar/session/state/MotionProcessor.java @@ -0,0 +1,81 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.session.state; + +import org.traccar.model.Event; +import org.traccar.model.Position; +import org.traccar.reports.common.TripsConfig; + +public final class MotionProcessor { + + private MotionProcessor() { + } + + public static void updateState( + MotionState state, Position position, boolean newState, TripsConfig tripsConfig) { + + state.setEvent(null); + + boolean oldState = state.getMotionState(); + if (oldState == newState) { + if (state.getMotionTime() != null) { + long oldTime = state.getMotionTime().getTime(); + long newTime = position.getFixTime().getTime(); + + double distance = position.getDouble(Position.KEY_TOTAL_DISTANCE) - state.getMotionDistance(); + Boolean ignition = null; + if (tripsConfig.getUseIgnition() && position.hasAttribute(Position.KEY_IGNITION)) { + ignition = position.getBoolean(Position.KEY_IGNITION); + } + + boolean generateEvent = false; + if (newState) { + if (newTime - oldTime >= tripsConfig.getMinimalTripDuration() + || distance >= tripsConfig.getMinimalTripDistance()) { + generateEvent = true; + } + } else { + if (newTime - oldTime >= tripsConfig.getMinimalParkingDuration() + || ignition != null && !ignition) { + generateEvent = true; + } + } + + if (generateEvent) { + + String eventType = newState ? Event.TYPE_DEVICE_MOVING : Event.TYPE_DEVICE_STOPPED; + Event event = new Event(eventType, position); + + state.setMotionStreak(newState); + state.setMotionTime(null); + state.setMotionDistance(0); + state.setEvent(event); + + } + } + } else { + state.setMotionState(newState); + if (state.getMotionStreak() == newState) { + state.setMotionTime(null); + state.setMotionDistance(0); + } else { + state.setMotionTime(position.getFixTime()); + state.setMotionDistance(position.getDouble(Position.KEY_TOTAL_DISTANCE)); + } + } + } + +} diff --git a/src/main/java/org/traccar/session/state/MotionState.java b/src/main/java/org/traccar/session/state/MotionState.java new file mode 100644 index 000000000..6c917ad16 --- /dev/null +++ b/src/main/java/org/traccar/session/state/MotionState.java @@ -0,0 +1,101 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.session.state; + +import org.traccar.model.Device; +import org.traccar.model.Event; + +import java.util.Date; + +public class MotionState { + + public static MotionState fromDevice(Device device) { + MotionState state = new MotionState(); + state.motionStreak = device.getMotionStreak(); + state.motionState = device.getMotionState(); + state.motionTime = device.getMotionTime(); + state.motionDistance = device.getMotionDistance(); + return state; + } + + public void toDevice(Device device) { + device.setMotionStreak(motionStreak); + device.setMotionState(motionState); + device.setMotionTime(motionTime); + device.setMotionDistance(motionDistance); + } + + private boolean changed; + + public boolean isChanged() { + return changed; + } + + private boolean motionStreak; + + public boolean getMotionStreak() { + return motionStreak; + } + + public void setMotionStreak(boolean motionStreak) { + this.motionStreak = motionStreak; + changed = true; + } + + private boolean motionState; + + public boolean getMotionState() { + return motionState; + } + + public void setMotionState(boolean motionState) { + this.motionState = motionState; + changed = true; + } + + private Date motionTime; + + public Date getMotionTime() { + return motionTime; + } + + public void setMotionTime(Date motionTime) { + this.motionTime = motionTime; + changed = true; + } + + private double motionDistance; + + public double getMotionDistance() { + return motionDistance; + } + + public void setMotionDistance(double motionDistance) { + this.motionDistance = motionDistance; + changed = true; + } + + private Event event; + + public Event getEvent() { + return event; + } + + public void setEvent(Event event) { + this.event = event; + } + +} diff --git a/src/main/java/org/traccar/session/state/OverspeedProcessor.java b/src/main/java/org/traccar/session/state/OverspeedProcessor.java new file mode 100644 index 000000000..221b51ff5 --- /dev/null +++ b/src/main/java/org/traccar/session/state/OverspeedProcessor.java @@ -0,0 +1,71 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.session.state; + +import org.traccar.model.Event; +import org.traccar.model.Position; + +public final class OverspeedProcessor { + + public static final String ATTRIBUTE_SPEED = "speed"; + + private OverspeedProcessor() { + } + + public static void updateState( + OverspeedState state, Position position, + double speedLimit, double multiplier, long minimalDuration, long geofenceId) { + + state.setEvent(null); + + boolean oldState = state.getOverspeedState(); + if (oldState) { + boolean newState = position.getSpeed() > speedLimit * multiplier; + if (newState) { + checkEvent(state, position, speedLimit, minimalDuration); + } else { + state.setOverspeedState(false); + state.setOverspeedTime(null); + state.setOverspeedGeofenceId(0); + } + } else if (position != null && position.getSpeed() > speedLimit * multiplier) { + state.setOverspeedState(true); + state.setOverspeedTime(position.getFixTime()); + state.setOverspeedGeofenceId(geofenceId); + + checkEvent(state, position, speedLimit, minimalDuration); + } + } + + private static void checkEvent(OverspeedState state, Position position, double speedLimit, long minimalDuration) { + if (state.getOverspeedTime() != null) { + long oldTime = state.getOverspeedTime().getTime(); + long newTime = position.getFixTime().getTime(); + if (newTime - oldTime >= minimalDuration) { + + Event event = new Event(Event.TYPE_DEVICE_OVERSPEED, position); + event.set(ATTRIBUTE_SPEED, position.getSpeed()); + event.set(Position.KEY_SPEED_LIMIT, speedLimit); + event.setGeofenceId(state.getOverspeedGeofenceId()); + + state.setOverspeedTime(null); + state.setOverspeedGeofenceId(0); + state.setEvent(event); + + } + } + } +} diff --git a/src/main/java/org/traccar/session/state/OverspeedState.java b/src/main/java/org/traccar/session/state/OverspeedState.java new file mode 100644 index 000000000..340ede6d7 --- /dev/null +++ b/src/main/java/org/traccar/session/state/OverspeedState.java @@ -0,0 +1,88 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.session.state; + +import org.traccar.model.Device; +import org.traccar.model.Event; + +import java.util.Date; + +public class OverspeedState { + + public static OverspeedState fromDevice(Device device) { + OverspeedState state = new OverspeedState(); + state.overspeedState = device.getOverspeedState(); + state.overspeedTime = device.getOverspeedTime(); + state.overspeedGeofenceId = device.getOverspeedGeofenceId(); + return state; + } + + public void toDevice(Device device) { + device.setOverspeedState(overspeedState); + device.setOverspeedTime(overspeedTime); + device.setOverspeedGeofenceId(overspeedGeofenceId); + } + + private boolean changed; + + public boolean isChanged() { + return changed; + } + + private boolean overspeedState; + + public boolean getOverspeedState() { + return overspeedState; + } + + public void setOverspeedState(boolean overspeedState) { + this.overspeedState = overspeedState; + changed = true; + } + + private Date overspeedTime; + + public Date getOverspeedTime() { + return overspeedTime; + } + + public void setOverspeedTime(Date overspeedTime) { + this.overspeedTime = overspeedTime; + changed = true; + } + + private long overspeedGeofenceId; + + public long getOverspeedGeofenceId() { + return overspeedGeofenceId; + } + + public void setOverspeedGeofenceId(long overspeedGeofenceId) { + this.overspeedGeofenceId = overspeedGeofenceId; + changed = true; + } + + private Event event; + + public Event getEvent() { + return event; + } + + public void setEvent(Event event) { + this.event = event; + } + +} diff --git a/src/main/java/org/traccar/sms/HttpSmsClient.java b/src/main/java/org/traccar/sms/HttpSmsClient.java index 6234eabb8..a2a0dd57f 100644 --- a/src/main/java/org/traccar/sms/HttpSmsClient.java +++ b/src/main/java/org/traccar/sms/HttpSmsClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2018 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,26 +16,23 @@ */ package org.traccar.sms; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traccar.Context; +import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.helper.DataConverter; import org.traccar.notification.MessageException; -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.Invocation; -import javax.ws.rs.client.InvocationCallback; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.Invocation; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; public class HttpSmsClient implements SmsManager { - private static final Logger LOGGER = LoggerFactory.getLogger(HttpSmsClient.class); - + private final Client client; private final String url; private final String authorizationHeader; private final String authorization; @@ -43,14 +40,15 @@ public class HttpSmsClient implements SmsManager { private final boolean encode; private final MediaType mediaType; - public HttpSmsClient() { - url = Context.getConfig().getString(Keys.SMS_HTTP_URL); - authorizationHeader = Context.getConfig().getString(Keys.SMS_HTTP_AUTHORIZATION_HEADER); - if (Context.getConfig().hasKey(Keys.SMS_HTTP_AUTHORIZATION)) { - authorization = Context.getConfig().getString(Keys.SMS_HTTP_AUTHORIZATION); + public HttpSmsClient(Config config, Client client) { + this.client = client; + url = config.getString(Keys.SMS_HTTP_URL); + authorizationHeader = config.getString(Keys.SMS_HTTP_AUTHORIZATION_HEADER); + if (config.hasKey(Keys.SMS_HTTP_AUTHORIZATION)) { + authorization = config.getString(Keys.SMS_HTTP_AUTHORIZATION); } else { - String user = Context.getConfig().getString(Keys.SMS_HTTP_USER); - String password = Context.getConfig().getString(Keys.SMS_HTTP_PASSWORD); + String user = config.getString(Keys.SMS_HTTP_USER); + String password = config.getString(Keys.SMS_HTTP_PASSWORD); if (user != null && password != null) { authorization = "Basic " + DataConverter.printBase64((user + ":" + password).getBytes(StandardCharsets.UTF_8)); @@ -58,8 +56,11 @@ public class HttpSmsClient implements SmsManager { authorization = null; } } - template = Context.getConfig().getString(Keys.SMS_HTTP_TEMPLATE).trim(); - if (template.charAt(0) == '{' || template.charAt(0) == '[') { + template = config.getString(Keys.SMS_HTTP_TEMPLATE).trim(); + if (template.charAt(0) == '<') { + encode = false; + mediaType = MediaType.APPLICATION_XML_TYPE; + } else if (template.charAt(0) == '{' || template.charAt(0) == '[') { encode = false; mediaType = MediaType.APPLICATION_JSON_TYPE; } else { @@ -69,7 +70,7 @@ public class HttpSmsClient implements SmsManager { } private String prepareValue(String value) throws UnsupportedEncodingException { - return encode ? URLEncoder.encode(value, StandardCharsets.UTF_8.name()) : value; + return encode ? URLEncoder.encode(value, StandardCharsets.UTF_8) : value; } private String preparePayload(String destAddress, String message) { @@ -83,7 +84,7 @@ public class HttpSmsClient implements SmsManager { } private Invocation.Builder getRequestBuilder() { - Invocation.Builder builder = Context.getClient().target(url).request(); + Invocation.Builder builder = client.target(url).request(); if (authorization != null) { builder = builder.header(authorizationHeader, authorization); } @@ -91,26 +92,13 @@ public class HttpSmsClient implements SmsManager { } @Override - public void sendMessageSync(String destAddress, String message, boolean command) throws MessageException { - Response response = getRequestBuilder().post(Entity.entity(preparePayload(destAddress, message), mediaType)); - if (response.getStatus() / 100 != 2) { - throw new MessageException(response.readEntity(String.class)); - } - } - - @Override - public void sendMessageAsync(final String destAddress, final String message, final boolean command) { - getRequestBuilder().async().post( - Entity.entity(preparePayload(destAddress, message), mediaType), new InvocationCallback<String>() { - @Override - public void completed(String s) { - } - - @Override - public void failed(Throwable throwable) { - LOGGER.warn("SMS send failed", throwable); + public void sendMessage(String destAddress, String message, boolean command) throws MessageException { + try (Response response = getRequestBuilder() + .post(Entity.entity(preparePayload(destAddress, message), mediaType))) { + if (response.getStatus() / 100 != 2) { + throw new MessageException(response.readEntity(String.class)); } - }); + } } } diff --git a/src/main/java/org/traccar/sms/SmsManager.java b/src/main/java/org/traccar/sms/SmsManager.java index 3b0cbda7f..8cf99c9e8 100644 --- a/src/main/java/org/traccar/sms/SmsManager.java +++ b/src/main/java/org/traccar/sms/SmsManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org) * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,10 +20,6 @@ import org.traccar.notification.MessageException; public interface SmsManager { - void sendMessageSync( - String destAddress, String message, boolean command) throws InterruptedException, MessageException; - - void sendMessageAsync( - String destAddress, String message, boolean command); + void sendMessage(String destAddress, String message, boolean command) throws MessageException; } diff --git a/src/main/java/org/traccar/sms/SnsSmsClient.java b/src/main/java/org/traccar/sms/SnsSmsClient.java index bdd4104f5..ed5a325cc 100644 --- a/src/main/java/org/traccar/sms/SnsSmsClient.java +++ b/src/main/java/org/traccar/sms/SnsSmsClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org) * Copyright 2021 Subodh Ranadive (subodhranadive3103@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -27,7 +27,7 @@ import com.amazonaws.services.sns.model.PublishRequest; import com.amazonaws.services.sns.model.PublishResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.traccar.Context; +import org.traccar.config.Config; import org.traccar.config.Keys; import java.util.HashMap; @@ -38,23 +38,16 @@ public class SnsSmsClient implements SmsManager { private final AmazonSNSAsync snsClient; - public SnsSmsClient() { - if (Context.getConfig().hasKey(Keys.SMS_AWS_REGION) - && Context.getConfig().hasKey(Keys.SMS_AWS_ACCESS) - && Context.getConfig().hasKey(Keys.SMS_AWS_SECRET)) { - BasicAWSCredentials awsCredentials = - new BasicAWSCredentials(Context.getConfig().getString(Keys.SMS_AWS_ACCESS), - Context.getConfig().getString(Keys.SMS_AWS_SECRET)); - snsClient = AmazonSNSAsyncClientBuilder.standard() - .withRegion(Context.getConfig().getString(Keys.SMS_AWS_REGION)) - .withCredentials(new AWSStaticCredentialsProvider(awsCredentials)).build(); - } else { - throw new RuntimeException("SNS Not Configured Properly. Please provide valid config."); - } + public SnsSmsClient(Config config) { + BasicAWSCredentials awsCredentials = new BasicAWSCredentials( + config.getString(Keys.SMS_AWS_ACCESS), config.getString(Keys.SMS_AWS_SECRET)); + snsClient = AmazonSNSAsyncClientBuilder.standard() + .withRegion(config.getString(Keys.SMS_AWS_REGION)) + .withCredentials(new AWSStaticCredentialsProvider(awsCredentials)).build(); } @Override - public void sendMessageSync(String destAddress, String message, boolean command) { + public void sendMessage(String destAddress, String message, boolean command) { Map<String, MessageAttributeValue> smsAttributes = new HashMap<>(); smsAttributes.put("AWS.SNS.SMS.SenderID", new MessageAttributeValue().withStringValue("SNS").withDataType("String")); @@ -64,19 +57,15 @@ public class SnsSmsClient implements SmsManager { PublishRequest publishRequest = new PublishRequest().withMessage(message) .withPhoneNumber(destAddress).withMessageAttributes(smsAttributes); - snsClient.publishAsync(publishRequest, new AsyncHandler<PublishRequest, PublishResult>() { + snsClient.publishAsync(publishRequest, new AsyncHandler<>() { @Override public void onError(Exception exception) { LOGGER.error("SMS send failed", exception); } + @Override public void onSuccess(PublishRequest request, PublishResult result) { } }); } - - @Override - public void sendMessageAsync(String destAddress, String message, boolean command) { - sendMessageSync(destAddress, message, command); - } } diff --git a/src/main/java/org/traccar/speedlimit/OverpassSpeedLimitProvider.java b/src/main/java/org/traccar/speedlimit/OverpassSpeedLimitProvider.java index 429a47c76..60ad65f9e 100644 --- a/src/main/java/org/traccar/speedlimit/OverpassSpeedLimitProvider.java +++ b/src/main/java/org/traccar/speedlimit/OverpassSpeedLimitProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2020 - 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. @@ -15,19 +15,21 @@ */ package org.traccar.speedlimit; -import org.traccar.Context; import org.traccar.helper.UnitsConverter; -import javax.json.JsonArray; -import javax.json.JsonObject; -import javax.ws.rs.client.AsyncInvoker; -import javax.ws.rs.client.InvocationCallback; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.AsyncInvoker; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.InvocationCallback; public class OverpassSpeedLimitProvider implements SpeedLimitProvider { + private final Client client; private final String url; - public OverpassSpeedLimitProvider(String url) { + public OverpassSpeedLimitProvider(Client client, String url) { + this.client = client; this.url = url + "?data=[out:json];way[maxspeed](around:100.0,%f,%f);out%%20tags;"; } @@ -46,7 +48,7 @@ public class OverpassSpeedLimitProvider implements SpeedLimitProvider { @Override public void getSpeedLimit(double latitude, double longitude, SpeedLimitProviderCallback callback) { String formattedUrl = String.format(url, latitude, longitude); - AsyncInvoker invoker = Context.getClient().target(formattedUrl).request().async(); + AsyncInvoker invoker = client.target(formattedUrl).request().async(); invoker.get(new InvocationCallback<JsonObject>() { @Override public void completed(JsonObject json) { diff --git a/src/main/java/org/traccar/storage/DatabaseModule.java b/src/main/java/org/traccar/storage/DatabaseModule.java new file mode 100644 index 000000000..9d9e5bd5e --- /dev/null +++ b/src/main/java/org/traccar/storage/DatabaseModule.java @@ -0,0 +1,103 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.storage; + +import com.google.inject.AbstractModule; +import com.google.inject.Provides; +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import liquibase.Contexts; +import liquibase.Liquibase; +import liquibase.database.Database; +import liquibase.database.DatabaseFactory; +import liquibase.exception.LiquibaseException; +import liquibase.resource.DirectoryResourceAccessor; +import liquibase.resource.ResourceAccessor; +import org.traccar.config.Config; +import org.traccar.config.Keys; + +import jakarta.inject.Singleton; +import javax.sql.DataSource; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.net.URL; + +public class DatabaseModule extends AbstractModule { + + @Singleton + @Provides + public static DataSource provideDataSource( + Config config) throws ReflectiveOperationException, IOException, LiquibaseException { + + String driverFile = config.getString(Keys.DATABASE_DRIVER_FILE); + if (driverFile != null) { + ClassLoader classLoader = ClassLoader.getSystemClassLoader(); + try { + Method method = classLoader.getClass().getDeclaredMethod("addURL", URL.class); + method.setAccessible(true); + method.invoke(classLoader, new File(driverFile).toURI().toURL()); + } catch (NoSuchMethodException e) { + Method method = classLoader.getClass() + .getDeclaredMethod("appendToClassPathForInstrumentation", String.class); + method.setAccessible(true); + method.invoke(classLoader, driverFile); + } + } + + String driver = config.getString(Keys.DATABASE_DRIVER); + if (driver != null) { + Class.forName(driver); + } + + HikariConfig hikariConfig = new HikariConfig(); + hikariConfig.setDriverClassName(driver); + hikariConfig.setJdbcUrl(config.getString(Keys.DATABASE_URL)); + hikariConfig.setUsername(config.getString(Keys.DATABASE_USER)); + hikariConfig.setPassword(config.getString(Keys.DATABASE_PASSWORD)); + hikariConfig.setConnectionInitSql(config.getString(Keys.DATABASE_CHECK_CONNECTION)); + hikariConfig.setIdleTimeout(600000); + + int maxPoolSize = config.getInteger(Keys.DATABASE_MAX_POOL_SIZE); + if (maxPoolSize != 0) { + hikariConfig.setMaximumPoolSize(maxPoolSize); + } + + DataSource dataSource = new HikariDataSource(hikariConfig); + + if (config.hasKey(Keys.DATABASE_CHANGELOG)) { + + ResourceAccessor resourceAccessor = new DirectoryResourceAccessor(new File(".")); + + Database database = DatabaseFactory.getInstance().openDatabase( + config.getString(Keys.DATABASE_URL), + config.getString(Keys.DATABASE_USER), + config.getString(Keys.DATABASE_PASSWORD), + config.getString(Keys.DATABASE_DRIVER), + null, null, null, resourceAccessor); + + String changelog = config.getString(Keys.DATABASE_CHANGELOG); + + try (Liquibase liquibase = new Liquibase(changelog, resourceAccessor, database)) { + liquibase.clearCheckSums(); + liquibase.update(new Contexts()); + } + } + + return dataSource; + } + +} diff --git a/src/main/java/org/traccar/storage/DatabaseStorage.java b/src/main/java/org/traccar/storage/DatabaseStorage.java index d73dc7b25..d20429319 100644 --- a/src/main/java/org/traccar/storage/DatabaseStorage.java +++ b/src/main/java/org/traccar/storage/DatabaseStorage.java @@ -1,15 +1,37 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.traccar.storage; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.traccar.config.Config; +import org.traccar.model.BaseModel; +import org.traccar.model.Device; +import org.traccar.model.Group; +import org.traccar.model.GroupedModel; import org.traccar.model.Permission; import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; -import org.traccar.storage.query.Limit; import org.traccar.storage.query.Order; import org.traccar.storage.query.Request; +import jakarta.inject.Inject; import javax.sql.DataSource; import java.sql.SQLException; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.function.Function; @@ -17,22 +39,37 @@ import java.util.stream.Collectors; public class DatabaseStorage extends Storage { + private final Config config; private final DataSource dataSource; + private final ObjectMapper objectMapper; + private final String databaseType; - public DatabaseStorage(DataSource dataSource) { + @Inject + public DatabaseStorage(Config config, DataSource dataSource, ObjectMapper objectMapper) { + this.config = config; this.dataSource = dataSource; + this.objectMapper = objectMapper; + + try { + databaseType = dataSource.getConnection().getMetaData().getDatabaseProductName(); + } catch (SQLException e) { + throw new RuntimeException(e); + } } @Override public <T> List<T> getObjects(Class<T> clazz, Request request) throws StorageException { StringBuilder query = new StringBuilder("SELECT "); - query.append(formatColumns(request.getColumns(), clazz, "get", c -> c)); - query.append(" FROM ").append(getTableName(clazz)); + if (request.getColumns() instanceof Columns.All) { + query.append('*'); + } else { + query.append(formatColumns(request.getColumns().getColumns(clazz, "set"), c -> c)); + } + query.append(" FROM ").append(getStorageName(clazz)); query.append(formatCondition(request.getCondition())); query.append(formatOrder(request.getOrder())); - query.append(formatLimit(request.getLimit())); try { - QueryBuilder builder = QueryBuilder.create(dataSource, query.toString()); + QueryBuilder builder = QueryBuilder.create(config, dataSource, objectMapper, query.toString()); for (Map.Entry<String, Object> variable : getConditionVariables(request.getCondition()).entrySet()) { builder.setValue(variable.getKey(), variable.getValue()); } @@ -44,16 +81,17 @@ public class DatabaseStorage extends Storage { @Override public <T> long addObject(T entity, Request request) throws StorageException { + List<String> columns = request.getColumns().getColumns(entity.getClass(), "get"); StringBuilder query = new StringBuilder("INSERT INTO "); - query.append(getTableName(entity.getClass())); + query.append(getStorageName(entity.getClass())); query.append("("); - query.append(formatColumns(request.getColumns(), entity.getClass(), "set", c -> c)); + query.append(formatColumns(columns, c -> c)); query.append(") VALUES ("); - query.append(formatColumns(request.getColumns(), entity.getClass(), "set", c -> ':' + c)); + query.append(formatColumns(columns, c -> ':' + c)); query.append(")"); try { - QueryBuilder builder = QueryBuilder.create(dataSource, query.toString(), true); - builder.setObject(entity); + QueryBuilder builder = QueryBuilder.create(config, dataSource, objectMapper, query.toString(), true); + builder.setObject(entity, columns); return builder.executeUpdate(); } catch (SQLException e) { throw new StorageException(e); @@ -62,14 +100,15 @@ public class DatabaseStorage extends Storage { @Override public <T> void updateObject(T entity, Request request) throws StorageException { + List<String> columns = request.getColumns().getColumns(entity.getClass(), "get"); StringBuilder query = new StringBuilder("UPDATE "); - query.append(getTableName(entity.getClass())); + query.append(getStorageName(entity.getClass())); query.append(" SET "); - query.append(formatColumns(request.getColumns(), entity.getClass(), "set", c -> c + " = :" + c)); + query.append(formatColumns(columns, c -> c + " = :" + c)); query.append(formatCondition(request.getCondition())); try { - QueryBuilder builder = QueryBuilder.create(dataSource, query.toString()); - builder.setObject(entity); + QueryBuilder builder = QueryBuilder.create(config, dataSource, objectMapper, query.toString()); + builder.setObject(entity, columns); for (Map.Entry<String, Object> variable : getConditionVariables(request.getCondition()).entrySet()) { builder.setValue(variable.getKey(), variable.getValue()); } @@ -82,10 +121,10 @@ public class DatabaseStorage extends Storage { @Override public void removeObject(Class<?> clazz, Request request) throws StorageException { StringBuilder query = new StringBuilder("DELETE FROM "); - query.append(getTableName(clazz)); + query.append(getStorageName(clazz)); query.append(formatCondition(request.getCondition())); try { - QueryBuilder builder = QueryBuilder.create(dataSource, query.toString()); + QueryBuilder builder = QueryBuilder.create(config, dataSource, objectMapper, query.toString()); for (Map.Entry<String, Object> variable : getConditionVariables(request.getCondition()).entrySet()) { builder.setValue(variable.getKey(), variable.getValue()); } @@ -96,11 +135,25 @@ public class DatabaseStorage extends Storage { } @Override - public List<Permission> getPermissions(Class<?> ownerClass, Class<?> propertyClass) throws StorageException { + public List<Permission> getPermissions( + Class<? extends BaseModel> ownerClass, long ownerId, + Class<? extends BaseModel> propertyClass, long propertyId) throws StorageException { StringBuilder query = new StringBuilder("SELECT * FROM "); query.append(Permission.getStorageName(ownerClass, propertyClass)); + var conditions = new LinkedList<Condition>(); + if (ownerId > 0) { + conditions.add(new Condition.Equals(Permission.getKey(ownerClass), ownerId)); + } + if (propertyId > 0) { + conditions.add(new Condition.Equals(Permission.getKey(propertyClass), propertyId)); + } + Condition combinedCondition = Condition.merge(conditions); + query.append(formatCondition(combinedCondition)); try { - QueryBuilder builder = QueryBuilder.create(dataSource, query.toString()); + QueryBuilder builder = QueryBuilder.create(config, dataSource, objectMapper, query.toString()); + for (Map.Entry<String, Object> variable : getConditionVariables(combinedCondition).entrySet()) { + builder.setValue(variable.getKey(), variable.getValue()); + } return builder.executePermissionsQuery(); } catch (SQLException e) { throw new StorageException(e); @@ -115,7 +168,7 @@ public class DatabaseStorage extends Storage { query.append(permission.get().keySet().stream().map(key -> ':' + key).collect(Collectors.joining(", "))); query.append(")"); try { - QueryBuilder builder = QueryBuilder.create(dataSource, query.toString(), true); + QueryBuilder builder = QueryBuilder.create(config, dataSource, objectMapper, query.toString(), true); for (var entry : permission.get().entrySet()) { builder.setLong(entry.getKey(), entry.getValue()); } @@ -133,7 +186,7 @@ public class DatabaseStorage extends Storage { query.append(permission .get().keySet().stream().map(key -> key + " = :" + key).collect(Collectors.joining(" AND "))); try { - QueryBuilder builder = QueryBuilder.create(dataSource, query.toString(), true); + QueryBuilder builder = QueryBuilder.create(config, dataSource, objectMapper, query.toString(), true); for (var entry : permission.get().entrySet()) { builder.setLong(entry.getKey(), entry.getValue()); } @@ -143,7 +196,7 @@ public class DatabaseStorage extends Storage { } } - private String getTableName(Class<?> clazz) throws StorageException { + private String getStorageName(Class<?> clazz) throws StorageException { StorageName storageName = clazz.getAnnotation(StorageName.class); if (storageName == null) { throw new StorageException("StorageName annotation is missing"); @@ -154,32 +207,43 @@ public class DatabaseStorage extends Storage { private Map<String, Object> getConditionVariables(Condition genericCondition) { Map<String, Object> results = new HashMap<>(); if (genericCondition instanceof Condition.Compare) { - Condition.Compare condition = (Condition.Compare) genericCondition; + var condition = (Condition.Compare) genericCondition; if (condition.getValue() != null) { results.put(condition.getVariable(), condition.getValue()); } } else if (genericCondition instanceof Condition.Between) { - Condition.Between condition = (Condition.Between) genericCondition; + var condition = (Condition.Between) genericCondition; results.put(condition.getFromVariable(), condition.getFromValue()); results.put(condition.getToVariable(), condition.getToValue()); } else if (genericCondition instanceof Condition.Binary) { - Condition.Binary condition = (Condition.Binary) genericCondition; + var condition = (Condition.Binary) genericCondition; results.putAll(getConditionVariables(condition.getFirst())); results.putAll(getConditionVariables(condition.getSecond())); + } else if (genericCondition instanceof Condition.Permission) { + var condition = (Condition.Permission) genericCondition; + if (condition.getOwnerId() > 0) { + results.put(Permission.getKey(condition.getOwnerClass()), condition.getOwnerId()); + } else { + results.put(Permission.getKey(condition.getPropertyClass()), condition.getPropertyId()); + } + } else if (genericCondition instanceof Condition.LatestPositions) { + var condition = (Condition.LatestPositions) genericCondition; + if (condition.getDeviceId() > 0) { + results.put("deviceId", condition.getDeviceId()); + } } return results; } - private String formatColumns( - Columns columns, Class<?> clazz, String type, Function<String, String> mapper) { - return columns.getColumns(clazz, type).stream().map(mapper).collect(Collectors.joining(", ")); + private String formatColumns(List<String> columns, Function<String, String> mapper) { + return columns.stream().map(mapper).collect(Collectors.joining(", ")); } - private String formatCondition(Condition genericCondition) { + private String formatCondition(Condition genericCondition) throws StorageException { return formatCondition(genericCondition, true); } - private String formatCondition(Condition genericCondition, boolean appendWhere) { + private String formatCondition(Condition genericCondition, boolean appendWhere) throws StorageException { StringBuilder result = new StringBuilder(); if (genericCondition != null) { if (appendWhere) { @@ -187,7 +251,7 @@ public class DatabaseStorage extends Storage { } if (genericCondition instanceof Condition.Compare) { - Condition.Compare condition = (Condition.Compare) genericCondition; + var condition = (Condition.Compare) genericCondition; result.append(condition.getColumn()); result.append(" "); result.append(condition.getOperator()); @@ -196,7 +260,7 @@ public class DatabaseStorage extends Storage { } else if (genericCondition instanceof Condition.Between) { - Condition.Between condition = (Condition.Between) genericCondition; + var condition = (Condition.Between) genericCondition; result.append(condition.getColumn()); result.append(" BETWEEN :"); result.append(condition.getFromVariable()); @@ -205,13 +269,31 @@ public class DatabaseStorage extends Storage { } else if (genericCondition instanceof Condition.Binary) { - Condition.Binary condition = (Condition.Binary) genericCondition; + var condition = (Condition.Binary) genericCondition; result.append(formatCondition(condition.getFirst(), false)); result.append(" "); result.append(condition.getOperator()); result.append(" "); result.append(formatCondition(condition.getSecond(), false)); + } else if (genericCondition instanceof Condition.Permission) { + + var condition = (Condition.Permission) genericCondition; + result.append("id IN ("); + result.append(formatPermissionQuery(condition)); + result.append(")"); + + } else if (genericCondition instanceof Condition.LatestPositions) { + + var condition = (Condition.LatestPositions) genericCondition; + result.append("id IN ("); + result.append("SELECT positionId FROM "); + result.append(getStorageName(Device.class)); + if (condition.getDeviceId() > 0) { + result.append(" WHERE id = :deviceId"); + } + result.append(")"); + } } return result.toString(); @@ -225,16 +307,103 @@ public class DatabaseStorage extends Storage { if (order.getDescending()) { result.append(" DESC"); } + if (order.getLimit() > 0) { + if (databaseType.equals("Microsoft SQL Server")) { + result.append(" OFFSET 0 ROWS FETCH FIRST "); + result.append(order.getLimit()); + result.append(" ROWS ONLY"); + } else { + result.append(" LIMIT "); + result.append(order.getLimit()); + } + } } return result.toString(); } - private String formatLimit(Limit limit) { + private String formatPermissionQuery(Condition.Permission condition) throws StorageException { StringBuilder result = new StringBuilder(); - if (limit != null) { - result.append(" LIMIT "); - result.append(limit.getValue()); + + String outputKey; + String conditionKey; + if (condition.getOwnerId() > 0) { + outputKey = Permission.getKey(condition.getPropertyClass()); + conditionKey = Permission.getKey(condition.getOwnerClass()); + } else { + outputKey = Permission.getKey(condition.getOwnerClass()); + conditionKey = Permission.getKey(condition.getPropertyClass()); } + + String storageName = Permission.getStorageName(condition.getOwnerClass(), condition.getPropertyClass()); + result.append("SELECT "); + result.append(storageName).append('.').append(outputKey); + result.append(" FROM "); + result.append(storageName); + result.append(" WHERE "); + result.append(conditionKey); + result.append(" = :"); + result.append(conditionKey); + + if (condition.getIncludeGroups()) { + + boolean expandDevices; + String groupStorageName; + if (GroupedModel.class.isAssignableFrom(condition.getOwnerClass())) { + expandDevices = Device.class.isAssignableFrom(condition.getOwnerClass()); + groupStorageName = Permission.getStorageName(Group.class, condition.getPropertyClass()); + } else { + expandDevices = Device.class.isAssignableFrom(condition.getPropertyClass()); + groupStorageName = Permission.getStorageName(condition.getOwnerClass(), Group.class); + } + + result.append(" UNION "); + + result.append("SELECT DISTINCT "); + if (!expandDevices) { + if (outputKey.equals("groupId")) { + result.append("all_groups."); + } else { + result.append(groupStorageName).append('.'); + } + } + result.append(outputKey); + result.append(" FROM "); + result.append(groupStorageName); + + result.append(" INNER JOIN ("); + result.append("SELECT id as parentId, id as groupId FROM "); + result.append(getStorageName(Group.class)); + result.append(" UNION "); + result.append("SELECT groupId as parentId, id as groupId FROM "); + result.append(getStorageName(Group.class)); + result.append(" WHERE groupId IS NOT NULL"); + result.append(" UNION "); + result.append("SELECT g2.groupId as parentId, g1.id as groupId FROM "); + result.append(getStorageName(Group.class)); + result.append(" AS g2"); + result.append(" INNER JOIN "); + result.append(getStorageName(Group.class)); + result.append(" AS g1 ON g2.id = g1.groupId"); + result.append(" WHERE g2.groupId IS NOT NULL"); + result.append(") AS all_groups ON "); + result.append(groupStorageName); + result.append(".groupId = all_groups.parentId"); + + if (expandDevices) { + result.append(" INNER JOIN ("); + result.append("SELECT groupId as parentId, id as deviceId FROM "); + result.append(getStorageName(Device.class)); + result.append(" WHERE groupId IS NOT NULL"); + result.append(") AS devices ON all_groups.groupId = devices.parentId"); + } + + result.append(" WHERE "); + result.append(conditionKey); + result.append(" = :"); + result.append(conditionKey); + + } + return result.toString(); } diff --git a/src/main/java/org/traccar/storage/MemoryStorage.java b/src/main/java/org/traccar/storage/MemoryStorage.java index 9cfe30a2b..9b5db1209 100644 --- a/src/main/java/org/traccar/storage/MemoryStorage.java +++ b/src/main/java/org/traccar/storage/MemoryStorage.java @@ -1,36 +1,172 @@ +/* + * 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.storage; +import org.traccar.model.BaseModel; import org.traccar.model.Pair; import org.traccar.model.Permission; +import org.traccar.model.Server; +import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; +import java.beans.Introspector; +import java.lang.reflect.Method; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; public class MemoryStorage extends Storage { + private final Map<Class<?>, Map<Long, Object>> objects = new HashMap<>(); private final Map<Pair<Class<?>, Class<?>>, Set<Pair<Long, Long>>> permissions = new HashMap<>(); + private final AtomicLong increment = new AtomicLong(); + + public MemoryStorage() { + Server server = new Server(); + server.setId(1); + server.setRegistration(true); + objects.put(Server.class, Map.of(server.getId(), server)); + } + @Override public <T> List<T> getObjects(Class<T> clazz, Request request) { - return null; + return objects.computeIfAbsent(clazz, key -> new HashMap<>()).values().stream() + .filter(object -> checkCondition(request.getCondition(), object)) + .map(object -> (T) object) + .collect(Collectors.toList()); + } + + private boolean checkCondition(Condition genericCondition, Object object) { + if (genericCondition == null) { + return true; + } + + if (genericCondition instanceof Condition.Compare) { + + var condition = (Condition.Compare) genericCondition; + Object value = retrieveValue(object, condition.getVariable()); + int result = ((Comparable) value).compareTo(condition.getValue()); + switch (condition.getOperator()) { + case "<": + return result < 0; + case "<=": + return result <= 0; + case ">": + return result > 0; + case ">=": + return result >= 0; + case "=": + return result == 0; + default: + throw new RuntimeException("Unsupported comparison condition"); + } + + } else if (genericCondition instanceof Condition.Between) { + + var condition = (Condition.Between) genericCondition; + Object fromValue = retrieveValue(object, condition.getFromVariable()); + int fromResult = ((Comparable) fromValue).compareTo(condition.getFromValue()); + Object toValue = retrieveValue(object, condition.getToVariable()); + int toResult = ((Comparable) toValue).compareTo(condition.getToValue()); + return fromResult >= 0 && toResult <= 0; + + } else if (genericCondition instanceof Condition.Binary) { + + var condition = (Condition.Binary) genericCondition; + if (condition.getOperator().equals("AND")) { + return checkCondition(condition.getFirst(), object) && checkCondition(condition.getSecond(), object); + } else if (condition.getOperator().equals("OR")) { + return checkCondition(condition.getFirst(), object) || checkCondition(condition.getSecond(), object); + } + + } else if (genericCondition instanceof Condition.Permission) { + + var condition = (Condition.Permission) genericCondition; + long id = (Long) retrieveValue(object, "id"); + return getPermissionsSet(condition.getOwnerClass(), condition.getPropertyClass()).stream() + .anyMatch(pair -> { + if (condition.getOwnerId() > 0) { + return pair.getFirst() == condition.getOwnerId() && pair.getSecond() == id; + } else { + return pair.getFirst() == id && pair.getSecond() == condition.getPropertyId(); + } + }); + + } else if (genericCondition instanceof Condition.LatestPositions) { + + return false; + + } + + return false; + } + + private Object retrieveValue(Object object, String key) { + try { + Method method = object.getClass().getMethod( + "get" + Character.toUpperCase(key.charAt(0)) + key.substring(1)); + return method.invoke(object); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } } @Override public <T> long addObject(T entity, Request request) { - return 0; + long id = increment.incrementAndGet(); + objects.computeIfAbsent(entity.getClass(), key -> new HashMap<>()).put(id, entity); + return id; } @Override public <T> void updateObject(T entity, Request request) { + Set<String> columns = new HashSet<>(request.getColumns().getColumns(entity.getClass(), "get")); + Collection<Object> items; + if (request.getCondition() != null) { + long id = (Long) ((Condition.Equals) request.getCondition()).getValue(); + items = List.of(objects.computeIfAbsent(entity.getClass(), key -> new HashMap<>()).get(id)); + } else { + items = objects.computeIfAbsent(entity.getClass(), key -> new HashMap<>()).values(); + } + for (Method setter : entity.getClass().getMethods()) { + if (setter.getName().startsWith("set") && setter.getParameterCount() == 1 + && columns.contains(Introspector.decapitalize(setter.getName()))) { + try { + Method getter = entity.getClass().getMethod(setter.getName().replaceFirst("set", "get")); + Object value = getter.invoke(entity); + for (Object object : items) { + setter.invoke(object, value); + } + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + } } @Override public void removeObject(Class<?> clazz, Request request) { + long id = (Long) ((Condition.Equals) request.getCondition()).getValue(); + objects.computeIfAbsent(clazz, key -> new HashMap<>()).remove(id); } private Set<Pair<Long, Long>> getPermissionsSet(Class<?> ownerClass, Class<?> propertyClass) { @@ -38,8 +174,12 @@ public class MemoryStorage extends Storage { } @Override - public List<Permission> getPermissions(Class<?> ownerClass, Class<?> propertyClass) { + public List<Permission> getPermissions( + Class<? extends BaseModel> ownerClass, long ownerId, + Class<? extends BaseModel> propertyClass, long propertyId) { return getPermissionsSet(ownerClass, propertyClass).stream() + .filter(pair -> ownerId == 0 || pair.getFirst().equals(ownerId)) + .filter(pair -> propertyId == 0 || pair.getSecond().equals(propertyId)) .map(pair -> new Permission(ownerClass, pair.getFirst(), propertyClass, pair.getSecond())) .collect(Collectors.toList()); } diff --git a/src/main/java/org/traccar/storage/QueryBuilder.java b/src/main/java/org/traccar/storage/QueryBuilder.java index da8002f0b..2f4c07406 100644 --- a/src/main/java/org/traccar/storage/QueryBuilder.java +++ b/src/main/java/org/traccar/storage/QueryBuilder.java @@ -16,9 +16,11 @@ package org.traccar.storage; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.traccar.Context; +import org.traccar.config.Config; +import org.traccar.config.Keys; import org.traccar.model.Permission; import javax.sql.DataSource; @@ -40,17 +42,25 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +@SuppressWarnings("UnusedReturnValue") public final class QueryBuilder { private static final Logger LOGGER = LoggerFactory.getLogger(QueryBuilder.class); + private final Config config; + private final ObjectMapper objectMapper; + private final Map<String, List<Integer>> indexMap = new HashMap<>(); private Connection connection; private PreparedStatement statement; private final String query; private final boolean returnGeneratedKeys; - private QueryBuilder(DataSource dataSource, String query, boolean returnGeneratedKeys) throws SQLException { + private QueryBuilder( + Config config, DataSource dataSource, ObjectMapper objectMapper, + String query, boolean returnGeneratedKeys) throws SQLException { + this.config = config; + this.objectMapper = objectMapper; this.query = query; this.returnGeneratedKeys = returnGeneratedKeys; if (query != null) { @@ -125,13 +135,15 @@ public final class QueryBuilder { return parsedQuery.toString(); } - public static QueryBuilder create(DataSource dataSource, String query) throws SQLException { - return new QueryBuilder(dataSource, query, false); + public static QueryBuilder create( + Config config, DataSource dataSource, ObjectMapper objectMapper, String query) throws SQLException { + return new QueryBuilder(config, dataSource, objectMapper, query, false); } public static QueryBuilder create( - DataSource dataSource, String query, boolean returnGeneratedKeys) throws SQLException { - return new QueryBuilder(dataSource, query, returnGeneratedKeys); + Config config, DataSource dataSource, ObjectMapper objectMapper, String query, + boolean returnGeneratedKeys) throws SQLException { + return new QueryBuilder(config, dataSource, objectMapper, query, returnGeneratedKeys); } private List<Integer> indexes(String name) { @@ -271,35 +283,32 @@ public final class QueryBuilder { return this; } - public QueryBuilder setObject(Object object) throws SQLException { - - Method[] methods = object.getClass().getMethods(); - - for (Method method : methods) { - if (method.getName().startsWith("get") && method.getParameterTypes().length == 0) { - String name = method.getName().substring(3); - try { - if (method.getReturnType().equals(boolean.class)) { - setBoolean(name, (Boolean) method.invoke(object)); - } else if (method.getReturnType().equals(int.class)) { - setInteger(name, (Integer) method.invoke(object)); - } else if (method.getReturnType().equals(long.class)) { - setLong(name, (Long) method.invoke(object), name.endsWith("Id")); - } else if (method.getReturnType().equals(double.class)) { - setDouble(name, (Double) method.invoke(object)); - } else if (method.getReturnType().equals(String.class)) { - setString(name, (String) method.invoke(object)); - } else if (method.getReturnType().equals(Date.class)) { - setDate(name, (Date) method.invoke(object)); - } else if (method.getReturnType().equals(byte[].class)) { - setBlob(name, (byte[]) method.invoke(object)); - } else { - setString(name, Context.getObjectMapper().writeValueAsString(method.invoke(object))); - } - } catch (IllegalAccessException | InvocationTargetException | JsonProcessingException error) { - LOGGER.warn("Get property error", error); + public QueryBuilder setObject(Object object, List<String> columns) throws SQLException { + + try { + for (String column : columns) { + Method method = object.getClass().getMethod( + "get" + Character.toUpperCase(column.charAt(0)) + column.substring(1)); + if (method.getReturnType().equals(boolean.class)) { + setBoolean(column, (Boolean) method.invoke(object)); + } else if (method.getReturnType().equals(int.class)) { + setInteger(column, (Integer) method.invoke(object)); + } else if (method.getReturnType().equals(long.class)) { + setLong(column, (Long) method.invoke(object), column.endsWith("Id")); + } else if (method.getReturnType().equals(double.class)) { + setDouble(column, (Double) method.invoke(object)); + } else if (method.getReturnType().equals(String.class)) { + setString(column, (String) method.invoke(object)); + } else if (method.getReturnType().equals(Date.class)) { + setDate(column, (Date) method.invoke(object)); + } else if (method.getReturnType().equals(byte[].class)) { + setBlob(column, (byte[]) method.invoke(object)); + } else { + setString(column, objectMapper.writeValueAsString(method.invoke(object))); } } + } catch (ReflectiveOperationException | JsonProcessingException e) { + LOGGER.warn("Set object error", e); } return this; @@ -309,15 +318,6 @@ public final class QueryBuilder { void process(T object, ResultSet resultSet) throws SQLException; } - public <T> T executeQuerySingle(Class<T> clazz) throws SQLException { - List<T> result = executeQuery(clazz); - if (!result.isEmpty()) { - return result.iterator().next(); - } else { - return null; - } - } - private <T> void addProcessors( List<ResultSetProcessor<T>> processors, final Class<?> parameterType, final Method method, final String name) { @@ -386,7 +386,7 @@ public final class QueryBuilder { String value = resultSet.getString(name); if (value != null && !value.isEmpty()) { try { - method.invoke(object, Context.getObjectMapper().readValue(value, parameterType)); + method.invoke(object, objectMapper.readValue(value, parameterType)); } catch (InvocationTargetException | IllegalAccessException | IOException error) { LOGGER.warn("Set property error", error); } @@ -395,6 +395,12 @@ public final class QueryBuilder { } } + private void logQuery() { + if (config.getBoolean(Keys.LOGGER_QUERIES)) { + LOGGER.info(query); + } + } + public <T> List<T> executeQuery(Class<T> clazz) throws SQLException { List<T> result = new LinkedList<>(); @@ -402,6 +408,8 @@ public final class QueryBuilder { try { + logQuery(); + try (ResultSet resultSet = statement.executeQuery()) { ResultSetMetaData resultMetaData = resultSet.getMetaData(); @@ -457,6 +465,7 @@ public final class QueryBuilder { if (query != null) { try { + logQuery(); statement.execute(); if (returnGeneratedKeys) { ResultSet resultSet = statement.getGeneratedKeys(); @@ -476,6 +485,7 @@ public final class QueryBuilder { List<Permission> result = new LinkedList<>(); if (query != null) { try { + logQuery(); try (ResultSet resultSet = statement.executeQuery()) { ResultSetMetaData resultMetaData = resultSet.getMetaData(); while (resultSet.next()) { diff --git a/src/main/java/org/traccar/storage/QueryExtended.java b/src/main/java/org/traccar/storage/QueryExtended.java deleted file mode 100644 index 3796f1f40..000000000 --- a/src/main/java/org/traccar/storage/QueryExtended.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2017 Anton Tananaev (anton@traccar.org) - * Copyright 2017 Andrey Kunitsyn (andrey@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.storage; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -public @interface QueryExtended { -} diff --git a/src/main/java/org/traccar/storage/Storage.java b/src/main/java/org/traccar/storage/Storage.java index 22c48cae0..55f5c22c0 100644 --- a/src/main/java/org/traccar/storage/Storage.java +++ b/src/main/java/org/traccar/storage/Storage.java @@ -1,5 +1,21 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.traccar.storage; +import org.traccar.model.BaseModel; import org.traccar.model.Permission; import org.traccar.storage.query.Request; @@ -16,12 +32,19 @@ public abstract class Storage { public abstract void removeObject(Class<?> clazz, Request request) throws StorageException; public abstract List<Permission> getPermissions( - Class<?> ownerClass, Class<?> propertyClass) throws StorageException; + Class<? extends BaseModel> ownerClass, long ownerId, + Class<? extends BaseModel> propertyClass, long propertyId) throws StorageException; public abstract void addPermission(Permission permission) throws StorageException; public abstract void removePermission(Permission permission) throws StorageException; + public List<Permission> getPermissions( + Class<? extends BaseModel> ownerClass, + Class<? extends BaseModel> propertyClass) throws StorageException { + return getPermissions(ownerClass, 0, propertyClass, 0); + } + public <T> T getObject(Class<T> clazz, Request request) throws StorageException { var objects = getObjects(clazz, request); return objects.isEmpty() ? null : objects.get(0); diff --git a/src/main/java/org/traccar/storage/StorageException.java b/src/main/java/org/traccar/storage/StorageException.java index 6c1e9c2ff..3f066cae6 100644 --- a/src/main/java/org/traccar/storage/StorageException.java +++ b/src/main/java/org/traccar/storage/StorageException.java @@ -1,3 +1,18 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.traccar.storage; public class StorageException extends Exception { diff --git a/src/main/java/org/traccar/storage/StorageName.java b/src/main/java/org/traccar/storage/StorageName.java index b6fa55e02..bf824c333 100644 --- a/src/main/java/org/traccar/storage/StorageName.java +++ b/src/main/java/org/traccar/storage/StorageName.java @@ -1,3 +1,18 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.traccar.storage; import java.lang.annotation.ElementType; diff --git a/src/main/java/org/traccar/storage/query/Columns.java b/src/main/java/org/traccar/storage/query/Columns.java index 196d2281c..a00400b36 100644 --- a/src/main/java/org/traccar/storage/query/Columns.java +++ b/src/main/java/org/traccar/storage/query/Columns.java @@ -1,8 +1,23 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.traccar.storage.query; -import org.traccar.storage.QueryExtended; import org.traccar.storage.QueryIgnore; +import java.beans.Introspector; import java.lang.reflect.Method; import java.util.Arrays; import java.util.LinkedList; @@ -21,9 +36,8 @@ public abstract class Columns { int parameterCount = type.equals("set") ? 1 : 0; if (method.getName().startsWith(type) && method.getParameterTypes().length == parameterCount && !method.isAnnotationPresent(QueryIgnore.class) - && !method.isAnnotationPresent(QueryExtended.class) && !method.getName().equals("getClass")) { - columns.add(method.getName().substring(3).toLowerCase()); + columns.add(Introspector.decapitalize(method.getName().substring(3))); } } return columns; diff --git a/src/main/java/org/traccar/storage/query/Condition.java b/src/main/java/org/traccar/storage/query/Condition.java index 82c8e8479..08b199052 100644 --- a/src/main/java/org/traccar/storage/query/Condition.java +++ b/src/main/java/org/traccar/storage/query/Condition.java @@ -1,14 +1,41 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.traccar.storage.query; +import org.traccar.model.GroupedModel; + +import java.util.List; + public interface Condition { - class Equals extends Compare { - public Equals(String column, String variable) { - this(column, variable, null); + static Condition merge(List<Condition> conditions) { + Condition result = null; + var iterator = conditions.iterator(); + if (iterator.hasNext()) { + result = iterator.next(); + while (iterator.hasNext()) { + result = new Condition.And(result, iterator.next()); + } } + return result; + } - public Equals(String column, String variable, Object value) { - super(column, "=", variable, value); + class Equals extends Compare { + public Equals(String column, Object value) { + super(column, "=", column, value); } } @@ -114,4 +141,71 @@ public interface Condition { } } + class Permission implements Condition { + private final Class<?> ownerClass; + private final long ownerId; + private final Class<?> propertyClass; + private final long propertyId; + private final boolean excludeGroups; + + private Permission( + Class<?> ownerClass, long ownerId, Class<?> propertyClass, long propertyId, boolean excludeGroups) { + this.ownerClass = ownerClass; + this.ownerId = ownerId; + this.propertyClass = propertyClass; + this.propertyId = propertyId; + this.excludeGroups = excludeGroups; + } + + public Permission(Class<?> ownerClass, long ownerId, Class<?> propertyClass) { + this(ownerClass, ownerId, propertyClass, 0, false); + } + + public Permission(Class<?> ownerClass, Class<?> propertyClass, long propertyId) { + this(ownerClass, 0, propertyClass, propertyId, false); + } + + public Permission excludeGroups() { + return new Permission(this.ownerClass, this.ownerId, this.propertyClass, this.propertyId, true); + } + + public Class<?> getOwnerClass() { + return ownerClass; + } + + public long getOwnerId() { + return ownerId; + } + + public Class<?> getPropertyClass() { + return propertyClass; + } + + public long getPropertyId() { + return propertyId; + } + + public boolean getIncludeGroups() { + boolean ownerGroupModel = GroupedModel.class.isAssignableFrom(ownerClass); + boolean propertyGroupModel = GroupedModel.class.isAssignableFrom(propertyClass); + return (ownerGroupModel || propertyGroupModel) && !excludeGroups; + } + } + + class LatestPositions implements Condition { + private final long deviceId; + + public LatestPositions(long deviceId) { + this.deviceId = deviceId; + } + + public LatestPositions() { + this(0); + } + + public long getDeviceId() { + return deviceId; + } + } + } diff --git a/src/main/java/org/traccar/storage/query/Limit.java b/src/main/java/org/traccar/storage/query/Limit.java index 4a20f0ce2..9673e5426 100644 --- a/src/main/java/org/traccar/storage/query/Limit.java +++ b/src/main/java/org/traccar/storage/query/Limit.java @@ -1,3 +1,18 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.traccar.storage.query; public class Limit { diff --git a/src/main/java/org/traccar/storage/query/Order.java b/src/main/java/org/traccar/storage/query/Order.java index 6852c5ba8..f10970381 100644 --- a/src/main/java/org/traccar/storage/query/Order.java +++ b/src/main/java/org/traccar/storage/query/Order.java @@ -1,17 +1,34 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.traccar.storage.query; public class Order { private final String column; private final boolean descending; + private final int limit; public Order(String column) { - this(false, column); + this(column, false, 0); } - public Order(boolean descending, String column) { + public Order(String column, boolean descending, int limit) { this.column = column; this.descending = descending; + this.limit = limit; } public String getColumn() { @@ -22,4 +39,8 @@ public class Order { return descending; } + public int getLimit() { + return limit; + } + } diff --git a/src/main/java/org/traccar/storage/query/Request.java b/src/main/java/org/traccar/storage/query/Request.java index a98dd48f7..b9c2c963c 100644 --- a/src/main/java/org/traccar/storage/query/Request.java +++ b/src/main/java/org/traccar/storage/query/Request.java @@ -1,3 +1,18 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.traccar.storage.query; public class Request { @@ -5,7 +20,6 @@ public class Request { private final Columns columns; private final Condition condition; private final Order order; - private final Limit limit; public Request(Columns columns) { this(columns, null, null); @@ -19,15 +33,14 @@ public class Request { this(columns, condition, null); } - public Request(Columns columns, Condition condition, Order order) { - this(columns, condition, order, null); + public Request(Columns columns, Order order) { + this(columns, null, order); } - public Request(Columns columns, Condition condition, Order order, Limit limit) { + public Request(Columns columns, Condition condition, Order order) { this.columns = columns; this.condition = condition; this.order = order; - this.limit = limit; } public Columns getColumns() { @@ -42,8 +55,4 @@ public class Request { return order; } - public Limit getLimit() { - return limit; - } - } diff --git a/src/main/java/org/traccar/web/ConsoleServlet.java b/src/main/java/org/traccar/web/ConsoleServlet.java index 0f3dcd8fd..0012ba077 100644 --- a/src/main/java/org/traccar/web/ConsoleServlet.java +++ b/src/main/java/org/traccar/web/ConsoleServlet.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 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. @@ -16,33 +16,39 @@ package org.traccar.web; import org.h2.server.web.ConnectionInfo; -import org.h2.server.web.WebServlet; +import org.h2.server.web.JakartaWebServlet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.traccar.Context; +import org.traccar.config.Config; import org.traccar.config.Keys; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -public class ConsoleServlet extends WebServlet { +public class ConsoleServlet extends JakartaWebServlet { private static final Logger LOGGER = LoggerFactory.getLogger(ConsoleServlet.class); + private final Config config; + + public ConsoleServlet(Config config) { + this.config = config; + } + @Override public void init() { super.init(); try { - Field field = WebServlet.class.getDeclaredField("server"); + Field field = JakartaWebServlet.class.getDeclaredField("server"); field.setAccessible(true); org.h2.server.web.WebServer server = (org.h2.server.web.WebServer) field.get(this); ConnectionInfo connectionInfo = new ConnectionInfo("Traccar|" - + Context.getConfig().getString(Keys.DATABASE_DRIVER) + "|" - + Context.getConfig().getString(Keys.DATABASE_URL) + "|" - + Context.getConfig().getString(Keys.DATABASE_USER)); + + config.getString(Keys.DATABASE_DRIVER) + "|" + + config.getString(Keys.DATABASE_URL) + "|" + + config.getString(Keys.DATABASE_USER)); Method method; diff --git a/src/main/java/org/traccar/web/ModernDefaultServlet.java b/src/main/java/org/traccar/web/ModernDefaultServlet.java new file mode 100644 index 000000000..a7c8cdb29 --- /dev/null +++ b/src/main/java/org/traccar/web/ModernDefaultServlet.java @@ -0,0 +1,59 @@ +/* + * 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.web; + +import org.eclipse.jetty.servlet.DefaultServlet; +import org.eclipse.jetty.util.resource.Resource; +import org.traccar.config.Config; +import org.traccar.config.Keys; + +import jakarta.inject.Inject; +import java.io.File; +import java.io.IOException; + +public class ModernDefaultServlet extends DefaultServlet { + + private Resource overrideResource; + + @Inject + public ModernDefaultServlet(Config config) { + String override = config.getString(Keys.WEB_OVERRIDE); + if (override != null) { + overrideResource = Resource.newResource(new File(override)); + } + } + + @Override + public Resource getResource(String pathInContext) { + if (overrideResource != null) { + try { + Resource override = overrideResource.addPath(pathInContext); + if (override.exists()) { + return override; + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return super.getResource(pathInContext.indexOf('.') < 0 ? "/" : pathInContext); + } + + @Override + public String getWelcomeFile(String pathInContext) { + return super.getWelcomeFile("/"); + } + +} diff --git a/src/main/java/org/traccar/web/OverrideFilter.java b/src/main/java/org/traccar/web/OverrideFilter.java new file mode 100644 index 000000000..f870c4147 --- /dev/null +++ b/src/main/java/org/traccar/web/OverrideFilter.java @@ -0,0 +1,87 @@ +/* + * 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.web; + +import com.google.inject.Provider; +import org.traccar.api.security.PermissionsService; +import org.traccar.model.Server; +import org.traccar.storage.StorageException; + +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Singleton +public class OverrideFilter implements Filter { + + private final Provider<PermissionsService> permissionsServiceProvider; + + @Inject + public OverrideFilter(Provider<PermissionsService> permissionsServiceProvider) { + this.permissionsServiceProvider = permissionsServiceProvider; + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + + if (((HttpServletRequest) request).getServletPath().startsWith("/api")) { + chain.doFilter(request, response); + return; + } + + ResponseWrapper wrappedResponse = new ResponseWrapper((HttpServletResponse) response); + + chain.doFilter(request, wrappedResponse); + + byte[] bytes = wrappedResponse.getCapture(); + if (bytes != null) { + if (wrappedResponse.getContentType() != null && wrappedResponse.getContentType().contains("text/html") + || ((HttpServletRequest) request).getPathInfo().endsWith("manifest.json")) { + + Server server; + try { + server = permissionsServiceProvider.get().getServer(); + } catch (StorageException e) { + throw new RuntimeException(e); + } + + String title = server.getString("title", "Traccar"); + String description = server.getString("description", "Traccar GPS Tracking System"); + String colorPrimary = server.getString("colorPrimary", "#1a237e"); + + String alteredContent = new String(wrappedResponse.getCapture()) + .replace("${title}", title) + .replace("${description}", description) + .replace("${colorPrimary}", colorPrimary); + + response.setContentLength(alteredContent.length()); + response.getOutputStream().write(alteredContent.getBytes()); + + } else { + response.getOutputStream().write(bytes); + } + } + } + +} diff --git a/src/main/java/org/traccar/web/ResponseWrapper.java b/src/main/java/org/traccar/web/ResponseWrapper.java new file mode 100644 index 000000000..a0eaf6788 --- /dev/null +++ b/src/main/java/org/traccar/web/ResponseWrapper.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.web; + +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.WriteListener; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletResponseWrapper; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public class ResponseWrapper extends HttpServletResponseWrapper { + + private final ByteArrayOutputStream capture; + private ServletOutputStream output; + + public ResponseWrapper(HttpServletResponse response) { + super(response); + capture = new ByteArrayOutputStream(response.getBufferSize()); + } + + @Override + public ServletOutputStream getOutputStream() { + if (output == null) { + output = new ServletOutputStream() { + @Override + public boolean isReady() { + return true; + } + + @Override + public void setWriteListener(WriteListener writeListener) { + } + + @Override + public void write(int b) { + capture.write(b); + } + + @Override + public void flush() throws IOException { + capture.flush(); + } + + @Override + public void close() throws IOException { + capture.close(); + } + }; + } + return output; + } + + @Override + public void flushBuffer() throws IOException { + super.flushBuffer(); + if (output != null) { + output.flush(); + } + } + + public byte[] getCapture() throws IOException { + if (output != null) { + output.close(); + return capture.toByteArray(); + } + return null; + } + +} diff --git a/src/main/java/org/traccar/web/ThrottlingFilter.java b/src/main/java/org/traccar/web/ThrottlingFilter.java new file mode 100644 index 000000000..1bad33db6 --- /dev/null +++ b/src/main/java/org/traccar/web/ThrottlingFilter.java @@ -0,0 +1,54 @@ +/* + * 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.web; + +import org.eclipse.jetty.servlets.DoSFilter; +import org.traccar.config.Config; +import org.traccar.config.Keys; + +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import jakarta.servlet.FilterConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; + +@Singleton +public class ThrottlingFilter extends DoSFilter { + + @Inject + private Config config; + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + super.init(filterConfig); + if (config.hasKey(Keys.WEB_MAX_REQUESTS_PER_SECOND)) { + setMaxRequestsPerSec(config.getInteger(Keys.WEB_MAX_REQUESTS_PER_SECOND)); + } + setMaxRequestMs(config.getInteger(Keys.WEB_MAX_REQUEST_SECONDS) * 1000L); + } + + @Override + protected String extractUserId(ServletRequest request) { + HttpSession session = ((HttpServletRequest) request).getSession(false); + if (session != null) { + var userId = session.getAttribute("userId"); + return userId != null ? userId.toString() : null; + } + return null; + } +} diff --git a/src/main/java/org/traccar/web/WebInjectionManagerFactory.java b/src/main/java/org/traccar/web/WebInjectionManagerFactory.java new file mode 100644 index 000000000..3e73c41ad --- /dev/null +++ b/src/main/java/org/traccar/web/WebInjectionManagerFactory.java @@ -0,0 +1,50 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.web; + +import org.glassfish.hk2.api.ServiceLocator; +import org.glassfish.jersey.inject.hk2.Hk2InjectionManagerFactory; +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.internal.inject.InjectionManagerFactory; +import org.jvnet.hk2.guice.bridge.api.GuiceBridge; +import org.jvnet.hk2.guice.bridge.api.GuiceIntoHK2Bridge; +import org.traccar.Main; + +import jakarta.annotation.Priority; + +@Priority(20) +public class WebInjectionManagerFactory implements InjectionManagerFactory { + + private final InjectionManagerFactory originalFactory = new Hk2InjectionManagerFactory(); + + private InjectionManager injectGuiceBridge(InjectionManager injectionManager) { + var serviceLocator = injectionManager.getInstance(ServiceLocator.class); + GuiceBridge.getGuiceBridge().initializeGuiceBridge(serviceLocator); + var guiceBridge = serviceLocator.getService(GuiceIntoHK2Bridge.class); + guiceBridge.bridgeGuiceInjector(Main.getInjector()); + return injectionManager; + } + + @Override + public InjectionManager create() { + return injectGuiceBridge(originalFactory.create()); + } + + @Override + public InjectionManager create(Object parent) { + return injectGuiceBridge(originalFactory.create(parent)); + } +} diff --git a/src/main/java/org/traccar/web/WebModule.java b/src/main/java/org/traccar/web/WebModule.java new file mode 100644 index 000000000..a32a6f447 --- /dev/null +++ b/src/main/java/org/traccar/web/WebModule.java @@ -0,0 +1,31 @@ +/* + * 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.web; + +import com.google.inject.servlet.ServletModule; +import org.traccar.api.AsyncSocketServlet; +import org.traccar.api.MediaFilter; + +public class WebModule extends ServletModule { + + @Override + protected void configureServlets() { + filter("/*").through(OverrideFilter.class); + filter("/api/*").through(ThrottlingFilter.class); + filter("/api/media/*").through(MediaFilter.class); + serve("/api/socket").with(AsyncSocketServlet.class); + } +} diff --git a/src/main/java/org/traccar/web/WebServer.java b/src/main/java/org/traccar/web/WebServer.java index 0ed5d013e..5f27f7662 100644 --- a/src/main/java/org/traccar/web/WebServer.java +++ b/src/main/java/org/traccar/web/WebServer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 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. @@ -15,6 +15,8 @@ */ package org.traccar.web; +import com.google.inject.Injector; +import com.google.inject.servlet.GuiceFilter; import org.eclipse.jetty.http.HttpCookie; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; @@ -35,35 +37,27 @@ import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer; -import org.glassfish.jersey.inject.hk2.ImmediateHk2InjectionManager; import org.glassfish.jersey.jackson.JacksonFeature; import org.glassfish.jersey.server.ResourceConfig; -import org.glassfish.jersey.server.spi.Container; -import org.glassfish.jersey.server.spi.ContainerLifecycleListener; import org.glassfish.jersey.servlet.ServletContainer; -import org.jvnet.hk2.guice.bridge.api.GuiceBridge; -import org.jvnet.hk2.guice.bridge.api.GuiceIntoHK2Bridge; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.traccar.Context; import org.traccar.LifecycleObject; -import org.traccar.Main; -import org.traccar.api.DateParameterConverterProvider; -import org.traccar.config.Config; -import org.traccar.api.AsyncSocketServlet; import org.traccar.api.CorsResponseFilter; -import org.traccar.api.MediaFilter; -import org.traccar.api.ObjectMapperProvider; +import org.traccar.api.DateParameterConverterProvider; import org.traccar.api.ResourceErrorHandler; -import org.traccar.api.security.SecurityRequestFilter; import org.traccar.api.resource.ServerResource; +import org.traccar.api.security.SecurityRequestFilter; +import org.traccar.config.Config; import org.traccar.config.Keys; - -import javax.servlet.DispatcherType; -import javax.servlet.ServletException; -import javax.servlet.SessionCookieConfig; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import org.traccar.helper.ObjectMapperContextResolver; + +import jakarta.servlet.DispatcherType; +import jakarta.servlet.ServletException; +import jakarta.servlet.SessionCookieConfig; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import javax.sql.DataSource; import java.io.File; import java.io.IOException; import java.io.Writer; @@ -74,10 +68,13 @@ public class WebServer implements LifecycleObject { private static final Logger LOGGER = LoggerFactory.getLogger(WebServer.class); - private Server server; - - private void initServer(Config config) { + private final Injector injector; + private final Config config; + private final Server server; + public WebServer(Injector injector, Config config) { + this.injector = injector; + this.config = config; String address = config.getString(Keys.WEB_ADDRESS); int port = config.getInteger(Keys.WEB_PORT); if (address == null) { @@ -85,22 +82,19 @@ public class WebServer implements LifecycleObject { } else { server = new Server(new InetSocketAddress(address, port)); } - } - - public WebServer(Config config) { - - initServer(config); ServletContextHandler servletHandler = new ServletContextHandler(ServletContextHandler.SESSIONS); + JettyWebSocketServletContainerInitializer.configure(servletHandler, null); + servletHandler.addFilter(GuiceFilter.class, "/*", EnumSet.allOf(DispatcherType.class)); - initApi(config, servletHandler); - initSessionConfig(config, servletHandler); + initApi(servletHandler); + initSessionConfig(servletHandler); if (config.getBoolean(Keys.WEB_CONSOLE)) { - servletHandler.addServlet(new ServletHolder(new ConsoleServlet()), "/console/*"); + servletHandler.addServlet(new ServletHolder(new ConsoleServlet(config)), "/console/*"); } - initWebApp(config, servletHandler); + initWebApp(servletHandler); servletHandler.setErrorHandler(new ErrorHandler() { @Override @@ -112,12 +106,12 @@ public class WebServer implements LifecycleObject { }); HandlerList handlers = new HandlerList(); - initClientProxy(config, handlers); + initClientProxy(handlers); handlers.addHandler(servletHandler); handlers.addHandler(new GzipHandler()); server.setHandler(handlers); - if (config.getBoolean(Keys.WEB_REQUEST_LOG_ENABLE)) { + if (config.hasKey(Keys.WEB_REQUEST_LOG_PATH)) { RequestLogWriter logWriter = new RequestLogWriter(config.getString(Keys.WEB_REQUEST_LOG_PATH)); logWriter.setAppend(true); logWriter.setRetainDays(config.getInteger(Keys.WEB_REQUEST_LOG_RETAIN_DAYS)); @@ -126,7 +120,7 @@ public class WebServer implements LifecycleObject { } } - private void initClientProxy(Config config, HandlerList handlers) { + private void initClientProxy(HandlerList handlers) { int port = config.getInteger(Keys.PROTOCOL_PORT.withPrefix("osmand")); if (port != 0) { ServletContextHandler servletHandler = new ServletContextHandler() { @@ -139,15 +133,15 @@ public class WebServer implements LifecycleObject { } } }; - ServletHolder servletHolder = new ServletHolder(new AsyncProxyServlet.Transparent()); + ServletHolder servletHolder = new ServletHolder(AsyncProxyServlet.Transparent.class); servletHolder.setInitParameter("proxyTo", "http://localhost:" + port); servletHandler.addServlet(servletHolder, "/"); handlers.addHandler(servletHandler); } } - private void initWebApp(Config config, ServletContextHandler servletHandler) { - ServletHolder servletHolder = new ServletHolder(DefaultServlet.class); + private void initWebApp(ServletContextHandler servletHandler) { + ServletHolder servletHolder = new ServletHolder(new ModernDefaultServlet(config)); servletHolder.setInitParameter("resourceBase", new File(config.getString(Keys.WEB_PATH)).getAbsolutePath()); servletHolder.setInitParameter("dirAllowed", "false"); if (config.getBoolean(Keys.WEB_DEBUG)) { @@ -162,10 +156,7 @@ public class WebServer implements LifecycleObject { servletHandler.addServlet(servletHolder, "/*"); } - private void initApi(Config config, ServletContextHandler servletHandler) { - servletHandler.addServlet(new ServletHolder(new AsyncSocketServlet()), "/api/socket"); - JettyWebSocketServletContainerInitializer.configure(servletHandler, null); - + private void initApi(ServletContextHandler servletHandler) { String mediaPath = config.getString(Keys.MEDIA_PATH); if (mediaPath != null) { ServletHolder servletHolder = new ServletHolder(DefaultServlet.class); @@ -173,39 +164,27 @@ public class WebServer implements LifecycleObject { servletHolder.setInitParameter("dirAllowed", "false"); servletHolder.setInitParameter("pathInfoOnly", "true"); servletHandler.addServlet(servletHolder, "/api/media/*"); - servletHandler.addFilter(MediaFilter.class, "/api/media/*", EnumSet.allOf(DispatcherType.class)); } ResourceConfig resourceConfig = new ResourceConfig(); resourceConfig.registerClasses( - JacksonFeature.class, ObjectMapperProvider.class, ResourceErrorHandler.class, - SecurityRequestFilter.class, CorsResponseFilter.class, DateParameterConverterProvider.class); + JacksonFeature.class, + ObjectMapperContextResolver.class, + DateParameterConverterProvider.class, + SecurityRequestFilter.class, + CorsResponseFilter.class, + ResourceErrorHandler.class); resourceConfig.packages(ServerResource.class.getPackage().getName()); - resourceConfig.register(new ContainerLifecycleListener() { - @Override - public void onStartup(Container container) { - var injectionManager = container.getApplicationHandler().getInjectionManager(); - var serviceLocator = ((ImmediateHk2InjectionManager) injectionManager).getServiceLocator(); - GuiceBridge.getGuiceBridge().initializeGuiceBridge(serviceLocator); - var guiceBridge = serviceLocator.getService(GuiceIntoHK2Bridge.class); - guiceBridge.bridgeGuiceInjector(Main.getInjector()); - } - - @Override - public void onReload(Container container) { - } - - @Override - public void onShutdown(Container container) { - } - }); + if (resourceConfig.getClasses().stream().filter(ServerResource.class::equals).findAny().isEmpty()) { + LOGGER.warn("Failed to load API resources"); + } servletHandler.addServlet(new ServletHolder(new ServletContainer(resourceConfig)), "/api/*"); } - private void initSessionConfig(Config config, ServletContextHandler servletHandler) { + private void initSessionConfig(ServletContextHandler servletHandler) { if (config.getBoolean(Keys.WEB_PERSIST_SESSION)) { DatabaseAdaptor databaseAdaptor = new DatabaseAdaptor(); - databaseAdaptor.setDatasource(Context.getDataManager().getDataSource()); + databaseAdaptor.setDatasource(injector.getInstance(DataSource.class)); JDBCSessionDataStoreFactory jdbcSessionDataStoreFactory = new JDBCSessionDataStoreFactory(); jdbcSessionDataStoreFactory.setDatabaseAdaptor(databaseAdaptor); SessionHandler sessionHandler = servletHandler.getSessionHandler(); @@ -214,14 +193,16 @@ public class WebServer implements LifecycleObject { sessionHandler.setSessionCache(sessionCache); } + SessionCookieConfig sessionCookieConfig = servletHandler.getServletContext().getSessionCookieConfig(); + int sessionTimeout = config.getInteger(Keys.WEB_SESSION_TIMEOUT); if (sessionTimeout > 0) { servletHandler.getSessionHandler().setMaxInactiveInterval(sessionTimeout); + sessionCookieConfig.setMaxAge(sessionTimeout); } String sameSiteCookie = config.getString(Keys.WEB_SAME_SITE_COOKIE); if (sameSiteCookie != null) { - SessionCookieConfig sessionCookieConfig = servletHandler.getServletContext().getSessionCookieConfig(); switch (sameSiteCookie.toLowerCase()) { case "lax": sessionCookieConfig.setComment(HttpCookie.SAME_SITE_LAX_COMMENT); @@ -240,21 +221,13 @@ public class WebServer implements LifecycleObject { } @Override - public void start() { - try { - server.start(); - } catch (Exception error) { - LOGGER.warn("Web server start failed", error); - } + public void start() throws Exception { + server.start(); } @Override - public void stop() { - try { - server.stop(); - } catch (Exception error) { - LOGGER.warn("Web server stop failed", error); - } + public void stop() throws Exception { + server.stop(); } } diff --git a/src/main/resources/META-INF/services/org.glassfish.jersey.internal.inject.InjectionManagerFactory b/src/main/resources/META-INF/services/org.glassfish.jersey.internal.inject.InjectionManagerFactory new file mode 100644 index 000000000..ce0a70630 --- /dev/null +++ b/src/main/resources/META-INF/services/org.glassfish.jersey.internal.inject.InjectionManagerFactory @@ -0,0 +1 @@ +org.traccar.web.WebInjectionManagerFactory diff --git a/src/main/resources/META-INF/services/org.jxls.expression.ExpressionEvaluatorFactory b/src/main/resources/META-INF/services/org.jxls.expression.ExpressionEvaluatorFactory new file mode 100644 index 000000000..75d628857 --- /dev/null +++ b/src/main/resources/META-INF/services/org.jxls.expression.ExpressionEvaluatorFactory @@ -0,0 +1 @@ +org.traccar.reports.common.ExpressionEvaluatorFactory diff --git a/src/test/java/org/traccar/BaseTest.java b/src/test/java/org/traccar/BaseTest.java index 0b2c616ce..2ace781f3 100644 --- a/src/test/java/org/traccar/BaseTest.java +++ b/src/test/java/org/traccar/BaseTest.java @@ -1,34 +1,69 @@ package org.traccar; -import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import org.traccar.config.Config; +import org.traccar.database.CommandsManager; import org.traccar.database.MediaManager; +import org.traccar.database.StatisticsManager; +import org.traccar.model.Device; +import org.traccar.session.ConnectionManager; +import org.traccar.session.DeviceSession; +import org.traccar.session.cache.CacheManager; -import java.util.HashMap; -import java.util.Map; +import java.net.SocketAddress; +import java.util.HashSet; -public class BaseTest { - - public static class MockMediaManager extends MediaManager { - Map<String, ByteBuf> files = new HashMap<>(); +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; - MockMediaManager() { - super(""); - } +public class BaseTest { - @Override - public String writeFile(String uniqueId, ByteBuf buf, String extension) { - String fileName = uniqueId + "/mock." + extension; - files.put(fileName, buf); - return fileName; - } + protected <T extends BaseProtocolDecoder> T inject(T decoder) throws Exception { + var config = new Config(); + decoder.setConfig(config); + var device = mock(Device.class); + when(device.getId()).thenReturn(1L); + var cacheManager = mock(CacheManager.class); + when(cacheManager.getConfig()).thenReturn(config); + when(cacheManager.getObject(eq(Device.class), anyLong())).thenReturn(device); + decoder.setCacheManager(cacheManager); + var connectionManager = mock(ConnectionManager.class); + var uniqueIdsProvided = new HashSet<Boolean>(); + when(connectionManager.getDeviceSession(any(), any(), any(), any(String[].class))).thenAnswer(invocation -> { + var mock = new DeviceSession(1L, "", mock(Protocol.class), mock(Channel.class), mock(SocketAddress.class)); + if (uniqueIdsProvided.isEmpty()) { + if (invocation.getArguments().length > 3) { + uniqueIdsProvided.add(true); + return mock; + } + return null; + } else { + return mock; + } + }); + decoder.setConnectionManager(connectionManager); + decoder.setStatisticsManager(mock(StatisticsManager.class)); + decoder.setMediaManager(mock(MediaManager.class)); + decoder.setCommandsManager(mock(CommandsManager.class)); + return decoder; + } - public ByteBuf readFile(String fileName) { - return files.get(fileName); - } + protected <T extends BaseFrameDecoder> T inject(T decoder) throws Exception { + return decoder; } - static { - Context.init(new TestIdentityManager(), new MockMediaManager()); + protected <T extends BaseProtocolEncoder> T inject(T encoder) throws Exception { + var device = mock(Device.class); + when(device.getId()).thenReturn(1L); + when(device.getUniqueId()).thenReturn("123456789012345"); + var cacheManager = mock(CacheManager.class); + when(cacheManager.getConfig()).thenReturn(mock(Config.class)); + when(cacheManager.getObject(eq(Device.class), anyLong())).thenReturn(device); + encoder.setCacheManager(cacheManager); + return encoder; } } diff --git a/src/test/java/org/traccar/ProtocolTest.java b/src/test/java/org/traccar/ProtocolTest.java index 353593c29..3b04b5e02 100644 --- a/src/test/java/org/traccar/ProtocolTest.java +++ b/src/test/java/org/traccar/ProtocolTest.java @@ -14,6 +14,7 @@ import org.traccar.helper.DataConverter; import org.traccar.model.CellTower; import org.traccar.model.Command; import org.traccar.model.Position; +import org.traccar.model.WifiAccessPoint; import java.nio.charset.StandardCharsets; import java.text.DateFormat; @@ -25,11 +26,11 @@ import java.util.List; import java.util.Map; import java.util.TimeZone; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; public class ProtocolTest extends BaseTest { @@ -111,7 +112,7 @@ public class ProtocolTest extends BaseTest { Object decodedObject = decoder.decode(null, null, object); Position position; if (decodedObject instanceof Collection) { - position = (Position) ((Collection) decodedObject).iterator().next(); + position = (Position) ((Collection<?>) decodedObject).iterator().next(); } else { position = (Position) decodedObject; } @@ -154,11 +155,11 @@ public class ProtocolTest extends BaseTest { private void verifyDecodedList(Object decodedObject, boolean checkLocation, Position expected) { - assertNotNull("list is null", decodedObject); - assertTrue("not a list", decodedObject instanceof List); - assertFalse("list is empty", ((List) decodedObject).isEmpty()); + assertNotNull(decodedObject, "list is null"); + assertTrue(decodedObject instanceof List, "not a list"); + assertFalse(((List<?>) decodedObject).isEmpty(), "list is empty"); - for (Object item : (List) decodedObject) { + for (Object item : (List<?>) decodedObject) { verifyDecodedPosition(item, checkLocation, false, expected); } @@ -166,8 +167,8 @@ public class ProtocolTest extends BaseTest { private void verifyDecodedPosition(Object decodedObject, boolean checkLocation, boolean checkAttributes, Position expected) { - assertNotNull("position is null", decodedObject); - assertTrue("not a position", decodedObject instanceof Position); + assertNotNull(decodedObject, "position is null"); + assertTrue(decodedObject instanceof Position, "not a position"); Position position = (Position) decodedObject; @@ -178,47 +179,46 @@ public class ProtocolTest extends BaseTest { if (expected.getFixTime() != null) { DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - assertEquals("time", dateFormat.format(expected.getFixTime()), dateFormat.format(position.getFixTime())); + assertEquals(dateFormat.format(expected.getFixTime()), dateFormat.format(position.getFixTime()), "time"); } - assertEquals("valid", expected.getValid(), position.getValid()); - assertEquals("latitude", expected.getLatitude(), position.getLatitude(), 0.00001); - assertEquals("longitude", expected.getLongitude(), position.getLongitude(), 0.00001); + assertEquals(expected.getValid(), position.getValid(), "valid"); + assertEquals(expected.getLatitude(), position.getLatitude(), 0.00001, "latitude"); + assertEquals(expected.getLongitude(), position.getLongitude(), 0.00001, "longitude"); } else { assertNotNull(position.getServerTime()); assertNotNull(position.getFixTime()); - assertTrue("year > 1999", position.getFixTime().after(new Date(915148800000L))); - assertTrue("time < +25 hours", - position.getFixTime().getTime() < System.currentTimeMillis() + 25 * 3600000); + assertTrue(position.getFixTime().after(new Date(915148800000L)), "year > 1999"); + assertTrue(position.getFixTime().getTime() < System.currentTimeMillis() + 25 * 3600000, "time < +25 h"); - assertTrue("latitude >= -90", position.getLatitude() >= -90); - assertTrue("latitude <= 90", position.getLatitude() <= 90); + assertTrue(position.getLatitude() >= -90, "latitude >= -90"); + assertTrue(position.getLatitude() <= 90, "latitude <= 90"); - assertTrue("longitude >= -180", position.getLongitude() >= -180); - assertTrue("longitude <= 180", position.getLongitude() <= 180); + assertTrue(position.getLongitude() >= -180, "longitude >= -180"); + assertTrue(position.getLongitude() <= 180, "longitude <= 180"); } - assertTrue("altitude >= -12262", position.getAltitude() >= -12262); - assertTrue("altitude <= 18000", position.getAltitude() <= 18000); + assertTrue(position.getAltitude() >= -12262, "altitude >= -12262"); + assertTrue(position.getAltitude() <= 18000, "altitude <= 18000"); - assertTrue("speed >= 0", position.getSpeed() >= 0); - assertTrue("speed <= 869", position.getSpeed() <= 869); + assertTrue(position.getSpeed() >= 0, "speed >= 0"); + assertTrue(position.getSpeed() <= 869, "speed <= 869"); - assertTrue("course >= 0", position.getCourse() >= 0); - assertTrue("course <= 360", position.getCourse() <= 360); + assertTrue(position.getCourse() >= 0, "course >= 0"); + assertTrue(position.getCourse() <= 360, "course <= 360"); - assertNotNull("protocol is null", position.getProtocol()); + assertNotNull(position.getProtocol(), "protocol is null"); - assertTrue("deviceId > 0", position.getDeviceId() > 0); + assertTrue(position.getDeviceId() > 0, "deviceId > 0"); } Map<String, Object> attributes = position.getAttributes(); if (checkAttributes) { - assertFalse("no attributes", attributes.isEmpty()); + assertFalse(attributes.isEmpty(), "no attributes"); } if (attributes.containsKey(Position.KEY_INDEX)) { @@ -310,23 +310,31 @@ public class ProtocolTest extends BaseTest { assertTrue(attributes.get(Position.KEY_RESULT) instanceof String); } - if (position.getNetwork() != null && position.getNetwork().getCellTowers() != null) { - for (CellTower cellTower : position.getNetwork().getCellTowers()) { - checkInteger(cellTower.getMobileCountryCode(), 0, 999); - checkInteger(cellTower.getMobileNetworkCode(), 0, 999); - checkInteger(cellTower.getLocationAreaCode(), 1, 65535); - checkInteger(cellTower.getCellId(), 0, 268435455); + if (position.getNetwork() != null) { + if (position.getNetwork().getCellTowers() != null) { + for (CellTower cellTower : position.getNetwork().getCellTowers()) { + checkInteger(cellTower.getMobileCountryCode(), 0, 999); + checkInteger(cellTower.getMobileNetworkCode(), 0, 999); + checkInteger(cellTower.getLocationAreaCode(), 1, 65535); + checkInteger(cellTower.getCellId(), 0, 268435455); + } + } + + if (position.getNetwork().getWifiAccessPoints() != null) { + for (WifiAccessPoint wifiAccessPoint : position.getNetwork().getWifiAccessPoints()) { + assertTrue(wifiAccessPoint.getMacAddress().matches("((\\p{XDigit}{2}):){5}(\\p{XDigit}{2})")); + } } } } private void checkInteger(Object value, int min, int max) { - assertNotNull("value is null", value); - assertTrue("not int or long", value instanceof Integer || value instanceof Long); + assertNotNull(value, "value is null"); + assertTrue(value instanceof Integer || value instanceof Long, "not int or long"); long number = ((Number) value).longValue(); - assertTrue("value too low", number >= min); - assertTrue("value too high", number <= max); + assertTrue(number >= min, "value too low"); + assertTrue(number <= max, "value too high"); } protected void verifyCommand( @@ -335,8 +343,8 @@ public class ProtocolTest extends BaseTest { } protected void verifyFrame(ByteBuf expected, Object object) { - assertNotNull("buffer is null", object); - assertTrue("not a buffer", object instanceof ByteBuf); + assertNotNull(object, "buffer is null"); + assertTrue(object instanceof ByteBuf, "not a buffer"); assertEquals(ByteBufUtil.hexDump(expected), ByteBufUtil.hexDump((ByteBuf) object)); } diff --git a/src/test/java/org/traccar/TestIdentityManager.java b/src/test/java/org/traccar/TestIdentityManager.java deleted file mode 100644 index 7d2865e74..000000000 --- a/src/test/java/org/traccar/TestIdentityManager.java +++ /dev/null @@ -1,80 +0,0 @@ -package org.traccar; - -import org.traccar.database.IdentityManager; -import org.traccar.model.Device; -import org.traccar.model.Position; - -public final class TestIdentityManager implements IdentityManager { - - private static Device createDevice() { - Device device = new Device(); - device.setId(1); - device.setName("test"); - device.setUniqueId("123456789012345"); - return device; - } - - @Override - public long addUnknownDevice(String uniqueId) { - return 1; - } - - @Override - public Device getById(long id) { - return createDevice(); - } - - @Override - public Device getByUniqueId(String uniqueId) { - return createDevice(); - } - - @Override - public String getDevicePassword(long id, String protocol, String defaultPassword) { - return defaultPassword; - } - - @Override - public Position getLastPosition(long deviceId) { - return null; - } - - @Override - public boolean isLatestPosition(Position position) { - return true; - } - - @Override - public boolean lookupAttributeBoolean( - long deviceId, String attributeName, boolean defaultValue, boolean lookupServer, boolean lookupConfig) { - return defaultValue; - } - - @Override - public String lookupAttributeString( - long deviceId, String attributeName, String defaultValue, boolean lookupServer, boolean lookupConfig) { - if (attributeName.equals("filter.skipAttributes")) { - return "alarm,result"; - } - return defaultValue; - } - - @Override - public int lookupAttributeInteger( - long deviceId, String attributeName, int defaultValue, boolean lookupServer, boolean lookupConfig) { - return defaultValue; - } - - @Override - public long lookupAttributeLong( - long deviceId, String attributeName, long defaultValue, boolean lookupServer, boolean lookupConfig) { - return defaultValue; - } - - @Override - public double lookupAttributeDouble( - long deviceId, String attributeName, double defaultValue, boolean lookupServer, boolean lookupConfig) { - return defaultValue; - } - -} diff --git a/src/test/java/org/traccar/WebDataHandlerTest.java b/src/test/java/org/traccar/WebDataHandlerTest.java deleted file mode 100644 index cfbd71f23..000000000 --- a/src/test/java/org/traccar/WebDataHandlerTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.traccar; - -import org.junit.Test; -import org.traccar.config.Config; -import org.traccar.config.Keys; -import org.traccar.model.Position; - -import static org.junit.Assert.assertEquals; - -public class WebDataHandlerTest extends ProtocolTest { - - @Test - public void testFormatRequest() throws Exception { - - Config config = new Config(); - config.setString(Keys.FORWARD_URL, "http://localhost/?fixTime={fixTime}&gprmc={gprmc}&name={name}"); - - Position position = position("2016-01-01 01:02:03.000", true, 20, 30); - - WebDataHandler handler = new WebDataHandler(config, Context.getIdentityManager(), null, null); - - assertEquals( - "http://localhost/?fixTime=1451610123000&gprmc=$GPRMC,010203.000,A,2000.0000,N,03000.0000,E,0.00,0.00,010116,,*05&name=test", - handler.formatRequest(position)); - - } - -} diff --git a/src/test/java/org/traccar/calendar/CalendarTest.java b/src/test/java/org/traccar/calendar/CalendarTest.java index 56406d4b8..4106f89a9 100644 --- a/src/test/java/org/traccar/calendar/CalendarTest.java +++ b/src/test/java/org/traccar/calendar/CalendarTest.java @@ -1,5 +1,9 @@ package org.traccar.calendar; +import net.fortuna.ical4j.data.ParserException; +import org.junit.jupiter.api.Test; +import org.traccar.model.Calendar; + import java.io.IOException; import java.sql.SQLException; import java.text.DateFormat; @@ -7,12 +11,8 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; -import org.junit.Test; -import org.traccar.model.Calendar; - -import net.fortuna.ical4j.data.ParserException; - -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; public class CalendarTest { @@ -46,14 +46,12 @@ public class CalendarTest { calendar.setData(calendarString.getBytes()); DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssX"); - Date date = format.parse("2016-12-13 22:59:59+05"); - assertTrue(!calendar.checkMoment(date)); - date = format.parse("2016-12-13 23:00:01+05"); - assertTrue(calendar.checkMoment(date)); + assertFalse(calendar.checkMoment(format.parse("2016-12-13 22:59:59+05"))); + assertTrue(calendar.checkMoment(format.parse("2016-12-13 23:00:01+05"))); + assertTrue(calendar.checkMoment(format.parse("2016-12-13 06:59:59+05"))); + assertFalse(calendar.checkMoment(format.parse("2016-12-13 07:00:01+05"))); - date = format.parse("2016-12-13 06:59:59+05"); - assertTrue(calendar.checkMoment(date)); - date = format.parse("2016-12-13 07:00:01+05"); - assertTrue(!calendar.checkMoment(date)); + var periods = calendar.findPeriods(format.parse("2016-12-13 06:59:59+05")); + assertFalse(periods.isEmpty()); } } diff --git a/src/test/java/org/traccar/config/ConfigTest.java b/src/test/java/org/traccar/config/ConfigTest.java index 13d0ffb04..f48e79acf 100644 --- a/src/test/java/org/traccar/config/ConfigTest.java +++ b/src/test/java/org/traccar/config/ConfigTest.java @@ -1,8 +1,8 @@ package org.traccar.config; -import org.junit.Test; -import static org.junit.Assert.assertEquals; -import org.traccar.config.Config; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; public class ConfigTest { diff --git a/src/test/java/org/traccar/database/GroupTreeTest.java b/src/test/java/org/traccar/database/GroupTreeTest.java index b547aab60..7c0d478f8 100644 --- a/src/test/java/org/traccar/database/GroupTreeTest.java +++ b/src/test/java/org/traccar/database/GroupTreeTest.java @@ -1,13 +1,13 @@ package org.traccar.database; -import org.junit.Test; +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.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class GroupTreeTest { diff --git a/src/test/java/org/traccar/forward/PositionForwarderUrlTest.java b/src/test/java/org/traccar/forward/PositionForwarderUrlTest.java new file mode 100644 index 000000000..f0e354620 --- /dev/null +++ b/src/test/java/org/traccar/forward/PositionForwarderUrlTest.java @@ -0,0 +1,42 @@ +package org.traccar.forward; + +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.model.Device; +import org.traccar.model.Position; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class PositionForwarderUrlTest extends ProtocolTest { + + @Test + public void testFormatRequest() throws Exception { + + Config config = new Config(); + config.setString(Keys.FORWARD_URL, "http://localhost/?fixTime={fixTime}&gprmc={gprmc}&name={name}"); + + Position position = position("2016-01-01 01:02:03.000", true, 20, 30); + + var device = mock(Device.class); + when(device.getId()).thenReturn(1L); + when(device.getName()).thenReturn("test"); + when(device.getUniqueId()).thenReturn("123456789012345"); + when(device.getStatus()).thenReturn(Device.STATUS_ONLINE); + + PositionData positionData = new PositionData(); + positionData.setPosition(position); + positionData.setDevice(device); + + PositionForwarderUrl forwarder = new PositionForwarderUrl(config, null, null); + + assertEquals( + "http://localhost/?fixTime=1451610123000&gprmc=$GPRMC,010203.000,A,2000.0000,N,03000.0000,E,0.00,0.00,010116,,*05&name=test", + forwarder.formatRequest(positionData)); + + } + +} diff --git a/src/test/java/org/traccar/geocoder/AddressFormatTest.java b/src/test/java/org/traccar/geocoder/AddressFormatTest.java index 0cc5168ef..b3a248bb3 100644 --- a/src/test/java/org/traccar/geocoder/AddressFormatTest.java +++ b/src/test/java/org/traccar/geocoder/AddressFormatTest.java @@ -1,8 +1,8 @@ package org.traccar.geocoder; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class AddressFormatTest { diff --git a/src/test/java/org/traccar/geocoder/GeocoderTest.java b/src/test/java/org/traccar/geocoder/GeocoderTest.java index 91431fd6a..1e1a98c1e 100644 --- a/src/test/java/org/traccar/geocoder/GeocoderTest.java +++ b/src/test/java/org/traccar/geocoder/GeocoderTest.java @@ -1,11 +1,13 @@ package org.traccar.geocoder; -import java.util.Locale; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; -import org.junit.Ignore; -import org.junit.Test; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; +import java.util.Locale; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class GeocoderTest { @@ -13,107 +15,109 @@ public class GeocoderTest { Locale.setDefault(Locale.US); } - @Ignore + private final Client client = ClientBuilder.newClient(); + + @Disabled @Test public void testGoogle() { - Geocoder geocoder = new GoogleGeocoder(null, null, 0, new AddressFormat()); + Geocoder geocoder = new GoogleGeocoder(client, null, null, 0, new AddressFormat()); String address = geocoder.getAddress(31.776797, 35.211489, null); assertEquals("1 Ibn Shaprut St, Jerusalem, Jerusalem District, IL", address); } - @Ignore + @Disabled @Test public void testNominatim() { - Geocoder geocoder = new NominatimGeocoder(null, null, null, 0, new AddressFormat()); + Geocoder geocoder = new NominatimGeocoder(client, null, null, null, 0, new AddressFormat()); String address = geocoder.getAddress(40.7337807, -73.9974401, null); assertEquals("35 West 9th Street, NYC, New York, US", address); } - @Ignore + @Disabled @Test public void testGisgraphy() { - Geocoder geocoder = new GisgraphyGeocoder(null, 0, new AddressFormat()); + Geocoder geocoder = new GisgraphyGeocoder(client, null, 0, new AddressFormat()); String address = geocoder.getAddress(48.8530000, 2.3400000, null); assertEquals("Rue du Jardinet, Paris, ÃŽle-de-France, FR", address); } - @Ignore + @Disabled @Test public void testOpenCage() { Geocoder geocoder = new OpenCageGeocoder( - "http://api.opencagedata.com/geocode/v1", "SECRET", null, 0, new AddressFormat()); + client, "http://api.opencagedata.com/geocode/v1", "SECRET", null, 0, new AddressFormat()); String address = geocoder.getAddress(34.116302, -118.051519, null); assertEquals("Charleston Road, California, US", address); } - @Ignore + @Disabled @Test public void testGeocodeFarm() { - Geocoder geocoder = new GeocodeFarmGeocoder(null, null, 0, new AddressFormat()); + Geocoder geocoder = new GeocodeFarmGeocoder(client, null, null, 0, new AddressFormat()); String address = geocoder.getAddress(34.116302, -118.051519, null); assertEquals("Estrella Avenue, Arcadia, California, United States", address); } - @Ignore + @Disabled @Test public void testGeocodeXyz() { - Geocoder geocoder = new GeocodeXyzGeocoder(null, 0, new AddressFormat()); + Geocoder geocoder = new GeocodeXyzGeocoder(client, null, 0, new AddressFormat()); String address = geocoder.getAddress(34.116302, -118.051519, null); assertEquals("605 ESTRELLA AVE, ARCADIA, California United States of America, US", address); } - @Ignore + @Disabled @Test public void testBan() { - Geocoder geocoder = new BanGeocoder(0, new AddressFormat("%f [%d], %c")); + Geocoder geocoder = new BanGeocoder(client, 0, new AddressFormat("%f [%d], %c")); String address = geocoder.getAddress(48.8575, 2.2944, null); assertEquals("8 Avenue Gustave Eiffel 75007 Paris [75, Paris, ÃŽle-de-France], FR", address); } - @Ignore + @Disabled @Test public void testHere() { - Geocoder geocoder = new HereGeocoder(null, "", "", null, 0, new AddressFormat()); + Geocoder geocoder = new HereGeocoder(client, null, "", "", null, 0, new AddressFormat()); String address = geocoder.getAddress(48.8575, 2.2944, null); assertEquals("6 Avenue Gustave Eiffel, Paris, ÃŽle-de-France, FRA", address); } - @Ignore + @Disabled @Test public void testMapmyIndia() { - Geocoder geocoder = new MapmyIndiaGeocoder("", "", 0, new AddressFormat("%f")); + Geocoder geocoder = new MapmyIndiaGeocoder(client, "", "", 0, new AddressFormat("%f")); String address = geocoder.getAddress(28.6129602407977, 77.2294557094574, null); assertEquals("New Delhi, Delhi. 1 m from India Gate pin-110001 (India)", address); } - @Ignore + @Disabled @Test public void testPositionStack() { - Geocoder geocoder = new PositionStackGeocoder("", 0, new AddressFormat("%f")); + Geocoder geocoder = new PositionStackGeocoder(client, "", 0, new AddressFormat("%f")); String address = geocoder.getAddress(28.6129602407977, 77.2294557094574, null); assertEquals("India Gate, New Delhi, India", address); } - @Ignore + @Disabled @Test public void testMapbox() { - Geocoder geocoder = new MapboxGeocoder("", 0, new AddressFormat("%f")); + Geocoder geocoder = new MapboxGeocoder(client, "", 0, new AddressFormat("%f")); String address = geocoder.getAddress(40.733, -73.989, null); assertEquals("120 East 13th Street, New York, New York 10003, United States", address); } - @Ignore + @Disabled @Test public void testMapTiler() { - Geocoder geocoder = new MapTilerGeocoder("", 0, new AddressFormat()); + Geocoder geocoder = new MapTilerGeocoder(client, "", 0, new AddressFormat()); String address = geocoder.getAddress(40.733, -73.989, null); assertEquals("East 13th Street, New York City, New York, United States", address); } - @Ignore + @Disabled @Test public void testGeoapify() { - Geocoder geocoder = new GeoapifyGeocoder("", null, 0, new AddressFormat()); + Geocoder geocoder = new GeoapifyGeocoder(client, "", null, 0, new AddressFormat()); String address = geocoder.getAddress(40.733, -73.989, null); assertEquals("114 East 13th Street, New York, New York, US", address); } diff --git a/src/test/java/org/traccar/geofence/GeofenceCircleTest.java b/src/test/java/org/traccar/geofence/GeofenceCircleTest.java index 259a8fb77..106b041fc 100644 --- a/src/test/java/org/traccar/geofence/GeofenceCircleTest.java +++ b/src/test/java/org/traccar/geofence/GeofenceCircleTest.java @@ -1,28 +1,27 @@ package org.traccar.geofence; -import java.text.ParseException; +import org.junit.jupiter.api.Test; -import org.junit.Test; +import java.text.ParseException; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; public class GeofenceCircleTest { @Test public void testCircleWkt() throws ParseException { String test = "CIRCLE (55.75414 37.6204, 100)"; - GeofenceGeometry geofenceGeometry = new GeofenceCircle(); - geofenceGeometry.fromWkt(test); + GeofenceGeometry geofenceGeometry = new GeofenceCircle(test); assertEquals(geofenceGeometry.toWkt(), test); } @Test public void testContainsCircle() throws ParseException { - String test = "CIRCLE (55.75414 37.6204, 100)"; - GeofenceGeometry geofenceGeometry = new GeofenceCircle(); - geofenceGeometry.fromWkt(test); - assertTrue(geofenceGeometry.containsPoint(55.75477, 37.62025)); - assertTrue(!geofenceGeometry.containsPoint(55.75545, 37.61921)); + GeofenceGeometry geofenceGeometry = new GeofenceCircle("CIRCLE (55.75414 37.6204, 100)"); + assertTrue(geofenceGeometry.containsPoint(null, null, 55.75477, 37.62025)); + assertFalse(geofenceGeometry.containsPoint(null, null, 55.75545, 37.61921)); } + } diff --git a/src/test/java/org/traccar/geofence/GeofencePolygonTest.java b/src/test/java/org/traccar/geofence/GeofencePolygonTest.java index cc9c46c94..bbf19cc38 100644 --- a/src/test/java/org/traccar/geofence/GeofencePolygonTest.java +++ b/src/test/java/org/traccar/geofence/GeofencePolygonTest.java @@ -1,12 +1,12 @@ package org.traccar.geofence; -import java.text.ParseException; +import org.junit.jupiter.api.Test; -import org.junit.Test; +import java.text.ParseException; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; public class GeofencePolygonTest { @@ -19,41 +19,34 @@ public class GeofencePolygonTest { @Test public void testPolygonWkt() throws ParseException { String test = "POLYGON ((55.75474 37.61823, 55.75513 37.61888, 55.7535 37.6222, 55.75315 37.62165))"; - GeofenceGeometry geofenceGeometry = new GeofencePolygon(); - geofenceGeometry.fromWkt(test); + GeofenceGeometry geofenceGeometry = new GeofencePolygon(test); assertEquals(geofenceGeometry.toWkt(), test); } @Test public void testContainsPolygon() throws ParseException { - String test = "POLYGON ((55.75474 37.61823, 55.75513 37.61888, 55.7535 37.6222, 55.75315 37.62165))"; - GeofenceGeometry geofenceGeometry = new GeofencePolygon(); - geofenceGeometry.fromWkt(test); - assertTrue(geofenceGeometry.containsPoint(55.75476, 37.61915)); - assertFalse(geofenceGeometry.containsPoint(55.75545, 37.61921)); - + GeofenceGeometry geofenceGeometry = new GeofencePolygon( + "POLYGON ((55.75474 37.61823, 55.75513 37.61888, 55.7535 37.6222, 55.75315 37.62165))"); + assertTrue(geofenceGeometry.containsPoint(null, null, 55.75476, 37.61915)); + assertFalse(geofenceGeometry.containsPoint(null, null, 55.75545, 37.61921)); } @Test public void testContainsPolygon180() throws ParseException { - String test = "POLYGON ((66.9494 179.838, 66.9508 -179.8496, 66.8406 -180.0014))"; - GeofenceGeometry geofenceGeometry = new GeofencePolygon(); - geofenceGeometry.fromWkt(test); - assertTrue(geofenceGeometry.containsPoint(66.9015, -180.0096)); - assertTrue(geofenceGeometry.containsPoint(66.9015, 179.991)); - assertFalse(geofenceGeometry.containsPoint(66.8368, -179.8792)); - + GeofenceGeometry geofenceGeometry = new GeofencePolygon( + "POLYGON ((66.9494 179.838, 66.9508 -179.8496, 66.8406 -180.0014))"); + assertTrue(geofenceGeometry.containsPoint(null, null, 66.9015, -180.0096)); + assertTrue(geofenceGeometry.containsPoint(null, null, 66.9015, 179.991)); + assertFalse(geofenceGeometry.containsPoint(null, null, 66.8368, -179.8792)); } @Test public void testContainsPolygon0() throws ParseException { - String test = "POLYGON ((51.1966 -0.6207, 51.1897 0.4147, 50.9377 0.5136, 50.8675 -0.6082))"; - GeofenceGeometry geofenceGeometry = new GeofencePolygon(); - geofenceGeometry.fromWkt(test); - assertTrue(geofenceGeometry.containsPoint(51.0466, -0.0165)); - assertTrue(geofenceGeometry.containsPoint(51.0466, 0.018)); - assertFalse(geofenceGeometry.containsPoint(50.9477, 0.5836)); - + GeofenceGeometry geofenceGeometry = new GeofencePolygon( + "POLYGON ((51.1966 -0.6207, 51.1897 0.4147, 50.9377 0.5136, 50.8675 -0.6082))"); + assertTrue(geofenceGeometry.containsPoint(null, null, 51.0466, -0.0165)); + assertTrue(geofenceGeometry.containsPoint(null, null, 51.0466, 0.018)); + assertFalse(geofenceGeometry.containsPoint(null, null, 50.9477, 0.5836)); } } diff --git a/src/test/java/org/traccar/geofence/GeofencePolylineTest.java b/src/test/java/org/traccar/geofence/GeofencePolylineTest.java index 1e9dcb7c3..9b7bbb7d1 100644 --- a/src/test/java/org/traccar/geofence/GeofencePolylineTest.java +++ b/src/test/java/org/traccar/geofence/GeofencePolylineTest.java @@ -1,47 +1,59 @@ package org.traccar.geofence; -import java.text.ParseException; +import org.junit.jupiter.api.Test; +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.model.Geofence; -import org.junit.Test; +import java.text.ParseException; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class GeofencePolylineTest { @Test public void testPolylineWkt() throws ParseException { String test = "LINESTRING (55.75474 37.61823, 55.75513 37.61888, 55.7535 37.6222, 55.75315 37.62165)"; - GeofenceGeometry geofenceGeometry = new GeofencePolyline(); - geofenceGeometry.fromWkt(test); + GeofenceGeometry geofenceGeometry = new GeofencePolyline(test); assertEquals(geofenceGeometry.toWkt(), test); } @Test public void testContainsPolyline1Interval() throws ParseException { - String test = "LINESTRING (56.83777 60.59833, 56.83766 60.5968)"; - GeofenceGeometry geofenceGeometry = new GeofencePolyline(test, 35); - assertTrue(geofenceGeometry.containsPoint(56.83801, 60.59748)); - ((GeofencePolyline) geofenceGeometry).setDistance(15); - assertTrue(!geofenceGeometry.containsPoint(56.83801, 60.59748)); + GeofenceGeometry geofenceGeometry = new GeofencePolyline( + "LINESTRING (56.83777 60.59833, 56.83766 60.5968)"); + Config config = mock(Config.class); + when(config.getDouble(Keys.GEOFENCE_POLYLINE_DISTANCE)).thenReturn(35.0); + assertTrue(geofenceGeometry.containsPoint(config, mock(Geofence.class), 56.83801, 60.59748)); + when(config.getDouble(Keys.GEOFENCE_POLYLINE_DISTANCE)).thenReturn(15.0); + assertFalse(geofenceGeometry.containsPoint(config, mock(Geofence.class), 56.83801, 60.59748)); } @Test public void testContainsPolyline3Intervals() throws ParseException { - String test = "LINESTRING (56.836 60.6126, 56.8393 60.6114, 56.83887 60.60811, 56.83782 60.5988)"; - GeofenceGeometry geofenceGeometry = new GeofencePolyline(test, 15); - assertTrue(geofenceGeometry.containsPoint(56.83847, 60.60458)); - assertTrue(!geofenceGeometry.containsPoint(56.83764, 60.59725)); - assertTrue(!geofenceGeometry.containsPoint(56.83861, 60.60822)); + GeofenceGeometry geofenceGeometry = new GeofencePolyline( + "LINESTRING (56.836 60.6126, 56.8393 60.6114, 56.83887 60.60811, 56.83782 60.5988)"); + Config config = mock(Config.class); + when(config.getDouble(Keys.GEOFENCE_POLYLINE_DISTANCE)).thenReturn(15.0); + assertTrue(geofenceGeometry.containsPoint(config, mock(Geofence.class), 56.83847, 60.60458)); + assertFalse(geofenceGeometry.containsPoint(config, mock(Geofence.class), 56.83764, 60.59725)); + assertFalse(geofenceGeometry.containsPoint(config, mock(Geofence.class), 56.83861, 60.60822)); } @Test public void testContainsPolylineNear180() throws ParseException { - String test = "LINESTRING (66.9494 179.838, 66.9508 -179.8496)"; - GeofenceGeometry geofenceGeometry = new GeofencePolyline(test, 25); - assertTrue(geofenceGeometry.containsPoint(66.95, 180.0)); - assertTrue(!geofenceGeometry.containsPoint(66.96, 180.0)); - assertTrue(!geofenceGeometry.containsPoint(66.9509, -179.83)); + GeofenceGeometry geofenceGeometry = new GeofencePolyline( + "LINESTRING (66.9494 179.838, 66.9508 -179.8496)"); + Config config = mock(Config.class); + when(config.getDouble(Keys.GEOFENCE_POLYLINE_DISTANCE)).thenReturn(25.0); + assertTrue(geofenceGeometry.containsPoint(config, mock(Geofence.class), 66.95, 180.0)); + assertFalse(geofenceGeometry.containsPoint(config, mock(Geofence.class), 66.96, 180.0)); + assertFalse(geofenceGeometry.containsPoint(config, mock(Geofence.class), 66.9509, -179.83)); } + } diff --git a/src/test/java/org/traccar/geolocation/GeolocationProviderTest.java b/src/test/java/org/traccar/geolocation/GeolocationProviderTest.java index 2729052d6..da5ae3340 100644 --- a/src/test/java/org/traccar/geolocation/GeolocationProviderTest.java +++ b/src/test/java/org/traccar/geolocation/GeolocationProviderTest.java @@ -1,24 +1,25 @@ package org.traccar.geolocation; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.traccar.BaseTest; import org.traccar.model.CellTower; import org.traccar.model.Network; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; public class GeolocationProviderTest extends BaseTest { - @Ignore - @Test - public void test() throws Exception { - testLocationProvider(); - } + private final Client client = ClientBuilder.newClient(); - public void testLocationProvider() throws Exception { - MozillaGeolocationProvider provider = new MozillaGeolocationProvider(null); + @Disabled + @Test + public void testMozilla() throws Exception { + MozillaGeolocationProvider provider = new MozillaGeolocationProvider(client, null); Network network = new Network(CellTower.from(208, 1, 2, 1234567)); diff --git a/src/test/java/org/traccar/handler/ComputedAttributesTest.java b/src/test/java/org/traccar/handler/ComputedAttributesTest.java index a76d8169b..e2af703c2 100644 --- a/src/test/java/org/traccar/handler/ComputedAttributesTest.java +++ b/src/test/java/org/traccar/handler/ComputedAttributesTest.java @@ -1,20 +1,20 @@ package org.traccar.handler; -import java.util.Date; - -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.config.Config; import org.traccar.model.Attribute; import org.traccar.model.Position; -import static org.junit.Assert.assertEquals; +import java.util.Date; + +import static org.junit.jupiter.api.Assertions.assertEquals; public class ComputedAttributesTest { @Test public void testComputedAttributes() { - ComputedAttributesHandler handler = new ComputedAttributesHandler(new Config(), null, null); + ComputedAttributesHandler handler = new ComputedAttributesHandler(new Config(), null); Date date = new Date(); Position position = new Position(); @@ -41,7 +41,7 @@ public class ComputedAttributesTest { attribute.setExpression("(bitFlag & 4) != 0"); assertEquals(true, handler.computeAttribute(attribute, position)); - attribute.setExpression("if (event == 42) \"lowBattery\""); + attribute.setExpression("event == 42 ? \"lowBattery\" : null"); assertEquals("lowBattery", handler.computeAttribute(attribute, position)); attribute.setExpression("speed > 5 && valid"); diff --git a/src/test/java/org/traccar/handler/DistanceHandlerTest.java b/src/test/java/org/traccar/handler/DistanceHandlerTest.java index f7c6e42cd..7d2f1e2e3 100644 --- a/src/test/java/org/traccar/handler/DistanceHandlerTest.java +++ b/src/test/java/org/traccar/handler/DistanceHandlerTest.java @@ -1,17 +1,19 @@ package org.traccar.handler; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.config.Config; import org.traccar.model.Position; +import org.traccar.session.cache.CacheManager; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; public class DistanceHandlerTest { @Test public void testCalculateDistance() { - DistanceHandler distanceHandler = new DistanceHandler(new Config(), null); + DistanceHandler distanceHandler = new DistanceHandler(new Config(), mock(CacheManager.class)); Position position = distanceHandler.handlePosition(new Position()); diff --git a/src/test/java/org/traccar/handler/FilterHandlerTest.java b/src/test/java/org/traccar/handler/FilterHandlerTest.java index ad8d244a6..36bb84f19 100644 --- a/src/test/java/org/traccar/handler/FilterHandlerTest.java +++ b/src/test/java/org/traccar/handler/FilterHandlerTest.java @@ -1,87 +1,99 @@ package org.traccar.handler; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.traccar.BaseTest; 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.Date; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class FilterHandlerTest extends BaseTest { - private FilterHandler passingHandler = new FilterHandler(new Config()); + private FilterHandler passingHandler; private FilterHandler filteringHandler; - @Before - public void before() { - Config config = new Config(); - config.setString(Keys.FILTER_INVALID, String.valueOf(true)); - config.setString(Keys.FILTER_ZERO, String.valueOf(true)); - config.setString(Keys.FILTER_DUPLICATE, String.valueOf(true)); - config.setString(Keys.FILTER_FUTURE, String.valueOf(5 * 60)); - config.setString(Keys.FILTER_APPROXIMATE, String.valueOf(true)); - config.setString(Keys.FILTER_STATIC, String.valueOf(true)); - config.setString(Keys.FILTER_DISTANCE, String.valueOf(10)); - config.setString(Keys.FILTER_MAX_SPEED, String.valueOf(500)); - config.setString(Keys.FILTER_SKIP_LIMIT, String.valueOf(10)); - config.setString(Keys.FILTER_SKIP_ATTRIBUTES_ENABLE, String.valueOf(true)); - filteringHandler = new FilterHandler(config); + @BeforeEach + public void passingHandler() { + var config = mock(Config.class); + when(config.getBoolean(Keys.FILTER_ENABLE)).thenReturn(true); + var cacheManager = mock(CacheManager.class); + when(cacheManager.getConfig()).thenReturn(config); + when(cacheManager.getObject(any(), anyLong())).thenReturn(mock(Device.class)); + passingHandler = new FilterHandler(config, cacheManager, null, null); } - private Position createPosition( - long deviceId, - Date time, - boolean valid, - double latitude, - double longitude, - double altitude, - double speed, - double course) { + @BeforeEach + public void filteringHandler() { + var config = mock(Config.class); + when(config.getBoolean(Keys.FILTER_ENABLE)).thenReturn(true); + when(config.getBoolean(Keys.FILTER_INVALID)).thenReturn(true); + when(config.getBoolean(Keys.FILTER_ZERO)).thenReturn(true); + when(config.getBoolean(Keys.FILTER_DUPLICATE)).thenReturn(true); + when(config.getLong(Keys.FILTER_FUTURE)).thenReturn(5 * 60L); + when(config.getBoolean(Keys.FILTER_APPROXIMATE)).thenReturn(true); + when(config.getBoolean(Keys.FILTER_STATIC)).thenReturn(true); + when(config.getInteger(Keys.FILTER_DISTANCE)).thenReturn(10); + when(config.getInteger(Keys.FILTER_MAX_SPEED)).thenReturn(500); + when(config.getLong(Keys.FILTER_SKIP_LIMIT)).thenReturn(10L); + when(config.getBoolean(Keys.FILTER_SKIP_ATTRIBUTES_ENABLE)).thenReturn(true); + when(config.getString(Keys.FILTER_SKIP_ATTRIBUTES.getKey())).thenReturn("alarm,result"); + var cacheManager = mock(CacheManager.class); + when(cacheManager.getConfig()).thenReturn(config); + when(cacheManager.getObject(any(), anyLong())).thenReturn(mock(Device.class)); + filteringHandler = new FilterHandler(config, cacheManager, null, null); + } + private Position createPosition(Date time, boolean valid, double speed) { Position position = new Position(); - position.setDeviceId(deviceId); + position.setDeviceId(0); position.setTime(time); position.setValid(valid); - position.setLatitude(latitude); - position.setLongitude(longitude); - position.setAltitude(altitude); + position.setLatitude(10); + position.setLongitude(10); + position.setAltitude(10); position.setSpeed(speed); - position.setCourse(course); + position.setCourse(10); return position; } @Test public void testFilter() { - Position position = createPosition(0, new Date(), true, 10, 10, 10, 10, 10); + Position position = createPosition(new Date(), true, 10); - assertNotNull(filteringHandler.handlePosition(position)); - assertNotNull(passingHandler.handlePosition(position)); + assertFalse(filteringHandler.filter(position)); + assertFalse(passingHandler.filter(position)); - position = createPosition(0, new Date(Long.MAX_VALUE), true, 10, 10, 10, 10, 10); + position = createPosition(new Date(Long.MAX_VALUE), true, 10); - assertNull(filteringHandler.handlePosition(position)); - assertNotNull(passingHandler.handlePosition(position)); + assertTrue(filteringHandler.filter(position)); + assertFalse(passingHandler.filter(position)); - position = createPosition(0, new Date(), false, 10, 10, 10, 10, 10); + position = createPosition(new Date(), false, 10); - assertNull(filteringHandler.handlePosition(position)); - assertNotNull(passingHandler.handlePosition(position)); + assertTrue(filteringHandler.filter(position)); + assertFalse(passingHandler.filter(position)); } @Test public void testSkipAttributes() { - Position position = createPosition(0, new Date(), true, 10, 10, 10, 0, 10); + Position position = createPosition(new Date(), true, 0); position.set(Position.KEY_ALARM, Position.ALARM_GENERAL); - assertNotNull(filteringHandler.handlePosition(position)); + assertFalse(filteringHandler.filter(position)); } diff --git a/src/test/java/org/traccar/handler/MotionHandlerTest.java b/src/test/java/org/traccar/handler/MotionHandlerTest.java index 9e0859664..10cdf6a90 100644 --- a/src/test/java/org/traccar/handler/MotionHandlerTest.java +++ b/src/test/java/org/traccar/handler/MotionHandlerTest.java @@ -1,16 +1,30 @@ package org.traccar.handler; -import static org.junit.Assert.assertEquals; - -import org.junit.Test; +import org.junit.jupiter.api.Test; +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 static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class MotionHandlerTest { @Test public void testCalculateMotion() { - MotionHandler motionHandler = new MotionHandler(0.01); + var cacheManager = mock(CacheManager.class); + when(cacheManager.getObject(eq(Device.class), anyLong())).thenReturn(mock(Device.class)); + var config = mock(Config.class); + when(config.getString(Keys.EVENT_MOTION_SPEED_THRESHOLD.getKey())).thenReturn("0.01"); + when(cacheManager.getConfig()).thenReturn(config); + + MotionHandler motionHandler = new MotionHandler(cacheManager); Position position = motionHandler.handlePosition(new Position()); diff --git a/src/test/java/org/traccar/handler/events/AlertEventHandlerTest.java b/src/test/java/org/traccar/handler/events/AlertEventHandlerTest.java index 3f0823245..66dc55c85 100644 --- a/src/test/java/org/traccar/handler/events/AlertEventHandlerTest.java +++ b/src/test/java/org/traccar/handler/events/AlertEventHandlerTest.java @@ -1,23 +1,24 @@ package org.traccar.handler.events; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import java.util.Map; - -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.BaseTest; -import org.traccar.TestIdentityManager; import org.traccar.config.Config; 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.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.Mockito.mock; public class AlertEventHandlerTest extends BaseTest { @Test public void testAlertEventHandler() { - AlertEventHandler alertEventHandler = new AlertEventHandler(new Config(), new TestIdentityManager()); + AlertEventHandler alertEventHandler = new AlertEventHandler(new Config(), mock(CacheManager.class)); Position position = new Position(); position.set(Position.KEY_ALARM, Position.ALARM_GENERAL); @@ -25,6 +26,7 @@ public class AlertEventHandlerTest extends BaseTest { assertNotNull(events); Event event = events.keySet().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 0ccf9f6b4..bc24e42f5 100644 --- a/src/test/java/org/traccar/handler/events/CommandResultEventHandlerTest.java +++ b/src/test/java/org/traccar/handler/events/CommandResultEventHandlerTest.java @@ -1,15 +1,15 @@ package org.traccar.handler.events; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import java.util.Map; - -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.BaseTest; import org.traccar.model.Event; import org.traccar.model.Position; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + public class CommandResultEventHandlerTest extends BaseTest { @Test diff --git a/src/test/java/org/traccar/handler/events/IgnitionEventHandlerTest.java b/src/test/java/org/traccar/handler/events/IgnitionEventHandlerTest.java index dade20fb8..972932df4 100644 --- a/src/test/java/org/traccar/handler/events/IgnitionEventHandlerTest.java +++ b/src/test/java/org/traccar/handler/events/IgnitionEventHandlerTest.java @@ -1,21 +1,22 @@ package org.traccar.handler.events; -import static org.junit.Assert.assertNull; - -import java.util.Map; - -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.BaseTest; -import org.traccar.TestIdentityManager; 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 { @Test public void testIgnitionEventHandler() { - IgnitionEventHandler ignitionEventHandler = new IgnitionEventHandler(new TestIdentityManager()); + IgnitionEventHandler ignitionEventHandler = new IgnitionEventHandler(mock(CacheManager.class)); Position position = new Position(); position.set(Position.KEY_IGNITION, true); diff --git a/src/test/java/org/traccar/handler/events/MaintenanceEventHandlerTest.java b/src/test/java/org/traccar/handler/events/MaintenanceEventHandlerTest.java new file mode 100644 index 000000000..5320be926 --- /dev/null +++ b/src/test/java/org/traccar/handler/events/MaintenanceEventHandlerTest.java @@ -0,0 +1,60 @@ +package org.traccar.handler.events; + +import org.junit.jupiter.api.Test; +import org.traccar.BaseTest; +import org.traccar.model.Maintenance; +import org.traccar.model.Position; +import org.traccar.session.cache.CacheManager; + +import java.util.Arrays; +import java.util.Date; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.anyLong; + +public class MaintenanceEventHandlerTest extends BaseTest { + + @Test + public void testMaintenanceEventHandler() { + Position lastPosition = new Position(); + lastPosition.setDeviceId(1); + lastPosition.setFixTime(new Date(0)); + + Position position = new Position(); + position.setDeviceId(1); + position.setFixTime(new Date(0)); + + var maintenance = mock(Maintenance.class); + when(maintenance.getType()).thenReturn(Position.KEY_TOTAL_DISTANCE); + var maintenances = Arrays.asList(maintenance); + + var cacheManager = mock(CacheManager.class); + when(cacheManager.getDeviceObjects(anyLong(), eq(Maintenance.class))).thenReturn(maintenances); + when(cacheManager.getPosition(anyLong())).thenReturn(lastPosition); + MaintenanceEventHandler eventHandler = new MaintenanceEventHandler(cacheManager); + + when(maintenance.getStart()).thenReturn(10000.0); + when(maintenance.getPeriod()).thenReturn(2000.0); + + lastPosition.set(Position.KEY_TOTAL_DISTANCE, 1999); + position.set(Position.KEY_TOTAL_DISTANCE, 2001); + assertTrue(eventHandler.analyzePosition(position).isEmpty()); + + lastPosition.set(Position.KEY_TOTAL_DISTANCE, 3999); + position.set(Position.KEY_TOTAL_DISTANCE, 4001); + assertTrue(eventHandler.analyzePosition(position).isEmpty()); + + lastPosition.set(Position.KEY_TOTAL_DISTANCE, 9999); + position.set(Position.KEY_TOTAL_DISTANCE, 10001); + assertTrue(eventHandler.analyzePosition(position).size() == 1); + + lastPosition.set(Position.KEY_TOTAL_DISTANCE, 11999); + position.set(Position.KEY_TOTAL_DISTANCE, 12001); + assertTrue(eventHandler.analyzePosition(position).size() == 1); + + } + +} diff --git a/src/test/java/org/traccar/handler/events/MotionEventHandlerTest.java b/src/test/java/org/traccar/handler/events/MotionEventHandlerTest.java index f57c16635..c61ae913d 100644 --- a/src/test/java/org/traccar/handler/events/MotionEventHandlerTest.java +++ b/src/test/java/org/traccar/handler/events/MotionEventHandlerTest.java @@ -1,119 +1,112 @@ package org.traccar.handler.events; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import org.junit.jupiter.api.Test; +import org.traccar.BaseTest; +import org.traccar.model.Event; +import org.traccar.model.Position; +import org.traccar.reports.common.TripsConfig; +import org.traccar.session.state.MotionProcessor; +import org.traccar.session.state.MotionState; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Map; import java.util.TimeZone; -import org.junit.Test; -import org.traccar.BaseTest; -import org.traccar.model.DeviceState; -import org.traccar.model.Event; -import org.traccar.model.Position; -import org.traccar.reports.model.TripsConfig; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; public class MotionEventHandlerTest extends BaseTest { - private Date date(String time) throws ParseException { + private Position position(String time, boolean motion, double distance, Boolean ignition) throws ParseException { + Position position = new Position(); DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - return dateFormat.parse(time); + position.setTime(dateFormat.parse(time)); + position.set(Position.KEY_MOTION, motion); + position.set(Position.KEY_TOTAL_DISTANCE, distance); + position.set(Position.KEY_IGNITION, ignition); + return position; + } + + private void verifyState(MotionState motionState, boolean state, long distance) { + assertEquals(state, motionState.getMotionState()); + assertEquals(distance, motionState.getMotionDistance(), 0.1); } @Test - public void testMotionWithPosition() throws Exception { - MotionEventHandler motionEventHandler = new MotionEventHandler( - null, null, new TripsConfig(500, 300 * 1000, 300 * 1000, 0, false, false, 0.01)); + public void testMotionWithPosition() throws ParseException { + TripsConfig tripsConfig = new TripsConfig(500, 300000, 300000, 0, false); - Position position = new Position(); - position.setTime(date("2017-01-01 00:00:00")); - position.set(Position.KEY_MOTION, true); - position.set(Position.KEY_TOTAL_DISTANCE, 0); - DeviceState deviceState = new DeviceState(); - deviceState.setMotionState(false); - deviceState.setMotionPosition(position); - Position nextPosition = new Position(); - - nextPosition.setTime(date("2017-01-01 00:02:00")); - nextPosition.set(Position.KEY_MOTION, true); - nextPosition.set(Position.KEY_TOTAL_DISTANCE, 200); - - Map<Event, Position> events = motionEventHandler.updateMotionState(deviceState, nextPosition); - assertNull(events); - - nextPosition.set(Position.KEY_TOTAL_DISTANCE, 600); - events = motionEventHandler.updateMotionState(deviceState, nextPosition); - assertNotNull(events); - Event event = events.keySet().iterator().next(); - assertEquals(Event.TYPE_DEVICE_MOVING, event.getType()); - assertTrue(deviceState.getMotionState()); - assertNull(deviceState.getMotionPosition()); - - deviceState.setMotionState(false); - deviceState.setMotionPosition(position); - nextPosition.setTime(date("2017-01-01 00:06:00")); - nextPosition.set(Position.KEY_TOTAL_DISTANCE, 200); - events = motionEventHandler.updateMotionState(deviceState, nextPosition); - assertNotNull(event); - event = events.keySet().iterator().next(); - assertEquals(Event.TYPE_DEVICE_MOVING, event.getType()); - assertTrue(deviceState.getMotionState()); - assertNull(deviceState.getMotionPosition()); + MotionState state = new MotionState(); + + MotionProcessor.updateState(state, position("2017-01-01 00:00:00", false, 0, null), false, tripsConfig); + assertNull(state.getEvent()); + verifyState(state, false, 0); + + MotionProcessor.updateState(state, position("2017-01-01 00:02:00", true, 100, null), true, tripsConfig); + assertNull(state.getEvent()); + verifyState(state, true, 100); + + MotionProcessor.updateState(state, position("2017-01-01 00:02:00", true, 700, null), true, tripsConfig); + assertEquals(Event.TYPE_DEVICE_MOVING, state.getEvent().getType()); + verifyState(state, true, 0); + + MotionProcessor.updateState(state, position("2017-01-01 00:03:00", false, 700, null), false, tripsConfig); + assertNull(state.getEvent()); + verifyState(state, false, 700); + + MotionProcessor.updateState(state, position("2017-01-01 00:10:00", false, 700, null), false, tripsConfig); + assertEquals(Event.TYPE_DEVICE_STOPPED, state.getEvent().getType()); + verifyState(state, false, 0); } @Test - public void testMotionWithStatus() throws Exception { - MotionEventHandler motionEventHandler = new MotionEventHandler( - null, null, new TripsConfig(500, 300 * 1000, 300 * 1000, 0, false, false, 0.01)); + public void testMotionFluctuation() throws ParseException { + TripsConfig tripsConfig = new TripsConfig(500, 300000, 300000, 0, false); - Position position = new Position(); - position.setTime(new Date(System.currentTimeMillis() - 360000)); - position.set(Position.KEY_MOTION, true); - DeviceState deviceState = new DeviceState(); - deviceState.setMotionState(false); - deviceState.setMotionPosition(position); - - Map<Event, Position> events = motionEventHandler.updateMotionState(deviceState); - - assertNotNull(events); - Event event = events.keySet().iterator().next(); - assertEquals(Event.TYPE_DEVICE_MOVING, event.getType()); - assertTrue(deviceState.getMotionState()); - assertNull(deviceState.getMotionPosition()); + MotionState state = new MotionState(); + + MotionProcessor.updateState(state, position("2017-01-01 00:00:00", false, 0, null), false, tripsConfig); + assertNull(state.getEvent()); + verifyState(state, false, 0); + + MotionProcessor.updateState(state, position("2017-01-01 00:02:00", true, 100, null), true, tripsConfig); + assertNull(state.getEvent()); + verifyState(state, true, 100); + + MotionProcessor.updateState(state, position("2017-01-01 00:02:00", true, 700, null), true, tripsConfig); + assertEquals(Event.TYPE_DEVICE_MOVING, state.getEvent().getType()); + verifyState(state, true, 0); + + MotionProcessor.updateState(state, position("2017-01-01 00:03:00", false, 700, null), false, tripsConfig); + assertNull(state.getEvent()); + verifyState(state, false, 700); + + MotionProcessor.updateState(state, position("2017-01-01 00:04:00", true, 1000, null), true, tripsConfig); + assertNull(state.getEvent()); + verifyState(state, true, 0); + + MotionProcessor.updateState(state, position("2017-01-01 00:06:00", true, 2000, null), true, tripsConfig); + assertNull(state.getEvent()); + verifyState(state, true, 0); } @Test - public void testStopWithPositionIgnition() throws Exception { - MotionEventHandler motionEventHandler = new MotionEventHandler( - null, null, new TripsConfig(500, 300 * 1000, 300 * 1000, 0, true, false, 0.01)); + public void testStopWithPositionIgnition() throws ParseException { + TripsConfig tripsConfig = new TripsConfig(500, 300000, 300000, 0, true); - Position position = new Position(); - position.setTime(date("2017-01-01 00:00:00")); - position.set(Position.KEY_MOTION, false); - position.set(Position.KEY_IGNITION, true); - DeviceState deviceState = new DeviceState(); - deviceState.setMotionState(true); - deviceState.setMotionPosition(position); - - Position nextPosition = new Position(); - nextPosition.setTime(date("2017-01-01 00:02:00")); - nextPosition.set(Position.KEY_MOTION, false); - nextPosition.set(Position.KEY_IGNITION, false); - - Map<Event, Position> events = motionEventHandler.updateMotionState(deviceState, nextPosition); - assertNotNull(events); - Event event = events.keySet().iterator().next(); - assertEquals(Event.TYPE_DEVICE_STOPPED, event.getType()); - assertFalse(deviceState.getMotionState()); - assertNull(deviceState.getMotionPosition()); + MotionState state = new MotionState(); + state.setMotionStreak(true); + state.setMotionState(true); + + MotionProcessor.updateState(state, position("2017-01-01 00:00:00", false, 100, true), false, tripsConfig); + assertNull(state.getEvent()); + verifyState(state, false, 100); + + MotionProcessor.updateState(state, position("2017-01-01 00:02:00", false, 100, false), false, tripsConfig); + assertEquals(Event.TYPE_DEVICE_STOPPED, state.getEvent().getType()); + verifyState(state, false, 0); } } diff --git a/src/test/java/org/traccar/handler/events/OverspeedEventHandlerTest.java b/src/test/java/org/traccar/handler/events/OverspeedEventHandlerTest.java index 515f37b5d..97d929551 100644 --- a/src/test/java/org/traccar/handler/events/OverspeedEventHandlerTest.java +++ b/src/test/java/org/traccar/handler/events/OverspeedEventHandlerTest.java @@ -1,128 +1,68 @@ package org.traccar.handler.events; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; +import org.junit.jupiter.api.Test; +import org.traccar.BaseTest; +import org.traccar.model.Event; +import org.traccar.model.Position; +import org.traccar.session.state.OverspeedProcessor; +import org.traccar.session.state.OverspeedState; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Map; import java.util.TimeZone; -import org.junit.Test; -import org.traccar.BaseTest; -import org.traccar.config.Config; -import org.traccar.config.Keys; -import org.traccar.model.DeviceState; -import org.traccar.model.Event; -import org.traccar.model.Position; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; public class OverspeedEventHandlerTest extends BaseTest { - private Date date(String time) throws ParseException { + private Position position(String time, double speed) throws ParseException { + Position position = new Position(); DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - return dateFormat.parse(time); + position.setTime(dateFormat.parse(time)); + position.setSpeed(speed); + return position; } - private void testOverspeedWithPosition(boolean notRepeat, long geofenceId) throws ParseException { - Config config = new Config(); - config.setString(Keys.EVENT_OVERSPEED_NOT_REPEAT, String.valueOf(notRepeat)); - config.setString(Keys.EVENT_OVERSPEED_MINIMAL_DURATION, String.valueOf(15)); - config.setString(Keys.EVENT_OVERSPEED_PREFER_LOWEST, String.valueOf(false)); - OverspeedEventHandler overspeedEventHandler = new OverspeedEventHandler(config, null, null); - - Position position = new Position(); - position.setTime(date("2017-01-01 00:00:00")); - position.setSpeed(50); - DeviceState deviceState = new DeviceState(); - deviceState.setOverspeedState(false); - - Map<Event, Position> events = overspeedEventHandler.updateOverspeedState(deviceState, position, 40, geofenceId); - assertNull(events); - assertFalse(deviceState.getOverspeedState()); - assertEquals(position, deviceState.getOverspeedPosition()); - assertEquals(geofenceId, deviceState.getOverspeedGeofenceId()); - - Position nextPosition = new Position(); - nextPosition.setTime(date("2017-01-01 00:00:10")); - nextPosition.setSpeed(55); - - events = overspeedEventHandler.updateOverspeedState(deviceState, nextPosition, 40, geofenceId); - assertNull(events); - - nextPosition.setTime(date("2017-01-01 00:00:20")); - - events = overspeedEventHandler.updateOverspeedState(deviceState, nextPosition, 40, geofenceId); - assertNotNull(events); - Event event = events.keySet().iterator().next(); - assertEquals(Event.TYPE_DEVICE_OVERSPEED, event.getType()); - assertEquals(50, event.getDouble("speed"), 0.1); - assertEquals(40, event.getDouble(OverspeedEventHandler.ATTRIBUTE_SPEED_LIMIT), 0.1); - assertEquals(geofenceId, event.getGeofenceId()); - - assertEquals(notRepeat, deviceState.getOverspeedState()); - assertNull(deviceState.getOverspeedPosition()); - assertEquals(0, deviceState.getOverspeedGeofenceId()); - - nextPosition.setTime(date("2017-01-01 00:00:30")); - events = overspeedEventHandler.updateOverspeedState(deviceState, nextPosition, 40, geofenceId); - assertNull(events); - assertEquals(notRepeat, deviceState.getOverspeedState()); + private void verifyState(OverspeedState overspeedState, boolean state, long geofenceId) { + assertEquals(state, overspeedState.getOverspeedState()); + assertEquals(geofenceId, overspeedState.getOverspeedGeofenceId()); + } - if (notRepeat) { - assertNull(deviceState.getOverspeedPosition()); - assertEquals(0, deviceState.getOverspeedGeofenceId()); - } else { - assertNotNull(deviceState.getOverspeedPosition()); - assertEquals(geofenceId, deviceState.getOverspeedGeofenceId()); - } + private void testOverspeedWithPosition(long geofenceId) throws ParseException { + OverspeedState state = new OverspeedState(); - nextPosition.setTime(date("2017-01-01 00:00:40")); - nextPosition.setSpeed(30); + OverspeedProcessor.updateState(state, position("2017-01-01 00:00:00", 50), 40, 1, 15000, geofenceId); + assertNull(state.getEvent()); + verifyState(state, true, geofenceId); - events = overspeedEventHandler.updateOverspeedState(deviceState, nextPosition, 40, geofenceId); - assertNull(events); - assertFalse(deviceState.getOverspeedState()); - assertNull(deviceState.getOverspeedPosition()); - assertEquals(0, deviceState.getOverspeedGeofenceId()); - } + OverspeedProcessor.updateState(state, position("2017-01-01 00:00:10", 55), 40, 1, 15000, geofenceId); + assertNull(state.getEvent()); - private void testOverspeedWithStatus(boolean notRepeat) { - Config config = new Config(); - config.setString(Keys.EVENT_OVERSPEED_NOT_REPEAT, String.valueOf(notRepeat)); - config.setString(Keys.EVENT_OVERSPEED_MINIMAL_DURATION, String.valueOf(15)); - config.setString(Keys.EVENT_OVERSPEED_PREFER_LOWEST, String.valueOf(false)); - OverspeedEventHandler overspeedEventHandler = new OverspeedEventHandler(config, null, null); + OverspeedProcessor.updateState(state, position("2017-01-01 00:00:20", 55), 40, 1, 15000, geofenceId); + assertNotNull(state.getEvent()); + assertEquals(Event.TYPE_DEVICE_OVERSPEED, state.getEvent().getType()); + assertEquals(55, state.getEvent().getDouble("speed"), 0.1); + assertEquals(40, state.getEvent().getDouble("speedLimit"), 0.1); + assertEquals(geofenceId, state.getEvent().getGeofenceId()); + verifyState(state, true, 0); - Position position = new Position(); - position.setTime(new Date(System.currentTimeMillis() - 30000)); - position.setSpeed(50); - DeviceState deviceState = new DeviceState(); - deviceState.setOverspeedState(false); - deviceState.setOverspeedPosition(position); + OverspeedProcessor.updateState(state, position("2017-01-01 00:00:30", 55), 40, 1, 15000, geofenceId); + assertNull(state.getEvent()); + verifyState(state, true, 0); - Map<Event, Position> events = overspeedEventHandler.updateOverspeedState(deviceState, 40); - - assertNotNull(events); - Event event = events.keySet().iterator().next(); - assertEquals(Event.TYPE_DEVICE_OVERSPEED, event.getType()); - assertEquals(notRepeat, deviceState.getOverspeedState()); + OverspeedProcessor.updateState(state, position("2017-01-01 00:00:30", 30), 40, 1, 15000, geofenceId); + assertNull(state.getEvent()); + verifyState(state, false, 0); } @Test public void testOverspeedEventHandler() throws Exception { - testOverspeedWithPosition(false, 0); - testOverspeedWithPosition(true, 0); - - testOverspeedWithPosition(false, 1); - testOverspeedWithPosition(true, 1); - - testOverspeedWithStatus(false); - testOverspeedWithStatus(true); + testOverspeedWithPosition(0); + testOverspeedWithPosition(1); } } diff --git a/src/test/java/org/traccar/helper/BcdUtilTest.java b/src/test/java/org/traccar/helper/BcdUtilTest.java index 86a32f725..440cd90d4 100644 --- a/src/test/java/org/traccar/helper/BcdUtilTest.java +++ b/src/test/java/org/traccar/helper/BcdUtilTest.java @@ -1,9 +1,9 @@ package org.traccar.helper; import io.netty.buffer.Unpooled; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class BcdUtilTest { diff --git a/src/test/java/org/traccar/helper/BitBufferTest.java b/src/test/java/org/traccar/helper/BitBufferTest.java index c2abad36d..3b3521213 100644 --- a/src/test/java/org/traccar/helper/BitBufferTest.java +++ b/src/test/java/org/traccar/helper/BitBufferTest.java @@ -1,8 +1,8 @@ package org.traccar.helper; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class BitBufferTest { diff --git a/src/test/java/org/traccar/helper/BitUtilTest.java b/src/test/java/org/traccar/helper/BitUtilTest.java index 90431bf55..803c327bc 100644 --- a/src/test/java/org/traccar/helper/BitUtilTest.java +++ b/src/test/java/org/traccar/helper/BitUtilTest.java @@ -1,10 +1,10 @@ package org.traccar.helper; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; public class BitUtilTest { diff --git a/src/test/java/org/traccar/helper/BufferUtilTest.java b/src/test/java/org/traccar/helper/BufferUtilTest.java index b539b5b28..707e419ec 100644 --- a/src/test/java/org/traccar/helper/BufferUtilTest.java +++ b/src/test/java/org/traccar/helper/BufferUtilTest.java @@ -2,15 +2,21 @@ package org.traccar.helper; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class BufferUtilTest { @Test + public void testReadSignedMagnitudeInt() { + ByteBuf buf = Unpooled.wrappedBuffer(DataConverter.parseHex("80000001")); + assertEquals(-1, BufferUtil.readSignedMagnitudeInt(buf)); + } + + @Test public void test1() { ByteBuf buf = Unpooled.copiedBuffer("abcdef", StandardCharsets.US_ASCII); assertEquals(2, BufferUtil.indexOf("cd", buf)); diff --git a/src/test/java/org/traccar/helper/ChecksumTest.java b/src/test/java/org/traccar/helper/ChecksumTest.java index 248f4dcae..51f62aba0 100644 --- a/src/test/java/org/traccar/helper/ChecksumTest.java +++ b/src/test/java/org/traccar/helper/ChecksumTest.java @@ -2,12 +2,12 @@ package org.traccar.helper; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class ChecksumTest { diff --git a/src/test/java/org/traccar/helper/DateBuilderTest.java b/src/test/java/org/traccar/helper/DateBuilderTest.java index b6323cc1d..35797a3ef 100644 --- a/src/test/java/org/traccar/helper/DateBuilderTest.java +++ b/src/test/java/org/traccar/helper/DateBuilderTest.java @@ -1,13 +1,13 @@ package org.traccar.helper; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.TimeZone; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class DateBuilderTest { diff --git a/src/test/java/org/traccar/helper/DateUtilTest.java b/src/test/java/org/traccar/helper/DateUtilTest.java index ec42e71ae..b5a4b1eab 100644 --- a/src/test/java/org/traccar/helper/DateUtilTest.java +++ b/src/test/java/org/traccar/helper/DateUtilTest.java @@ -1,13 +1,13 @@ package org.traccar.helper; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class DateUtilTest { diff --git a/src/test/java/org/traccar/helper/DistanceCalculatorTest.java b/src/test/java/org/traccar/helper/DistanceCalculatorTest.java index a7457b6c4..676fda5ea 100644 --- a/src/test/java/org/traccar/helper/DistanceCalculatorTest.java +++ b/src/test/java/org/traccar/helper/DistanceCalculatorTest.java @@ -1,8 +1,8 @@ package org.traccar.helper; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class DistanceCalculatorTest { diff --git a/src/test/java/org/traccar/helper/LogTest.java b/src/test/java/org/traccar/helper/LogTest.java index ef33c32ba..a264896b5 100644 --- a/src/test/java/org/traccar/helper/LogTest.java +++ b/src/test/java/org/traccar/helper/LogTest.java @@ -1,8 +1,8 @@ package org.traccar.helper; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class LogTest { diff --git a/src/test/java/org/traccar/helper/ObdDecoderTest.java b/src/test/java/org/traccar/helper/ObdDecoderTest.java index d5071bd51..2233ab5b3 100644 --- a/src/test/java/org/traccar/helper/ObdDecoderTest.java +++ b/src/test/java/org/traccar/helper/ObdDecoderTest.java @@ -1,8 +1,8 @@ package org.traccar.helper; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class ObdDecoderTest { diff --git a/src/test/java/org/traccar/helper/PatternBuilderTest.java b/src/test/java/org/traccar/helper/PatternBuilderTest.java index 4c76bc463..a8657a2e7 100644 --- a/src/test/java/org/traccar/helper/PatternBuilderTest.java +++ b/src/test/java/org/traccar/helper/PatternBuilderTest.java @@ -1,8 +1,8 @@ package org.traccar.helper; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class PatternBuilderTest { diff --git a/src/test/java/org/traccar/helper/PatternUtilTest.java b/src/test/java/org/traccar/helper/PatternUtilTest.java index 77660078a..ff8efe773 100644 --- a/src/test/java/org/traccar/helper/PatternUtilTest.java +++ b/src/test/java/org/traccar/helper/PatternUtilTest.java @@ -1,13 +1,13 @@ package org.traccar.helper; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class PatternUtilTest { - @Ignore + @Disabled @Test public void testCheckPattern() { diff --git a/src/test/java/org/traccar/helper/ServletHelperTest.java b/src/test/java/org/traccar/helper/ServletHelperTest.java deleted file mode 100644 index e419b6491..000000000 --- a/src/test/java/org/traccar/helper/ServletHelperTest.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.traccar.helper; - -import org.apache.struts.mock.MockHttpServletRequest; -import org.junit.Test; - -import java.util.HashMap; -import java.util.Map; - -import static org.junit.Assert.assertEquals; - -public class ServletHelperTest { - - @Test - public void testRetrieveRemoteAddressProxyMultiple() { - MockRequest request = new MockRequest(); - request.setRemoteAddress("147.120.1.5"); - request.addHeader("X-FORWARDED-FOR", "231.23.45.65, 10.20.10.33, 10.20.20.34"); - - assertEquals("231.23.45.65", ServletHelper.retrieveRemoteAddress(request)); - } - - @Test - public void testRetrieveRemoteAddressProxySingle() { - MockRequest request = new MockRequest(); - request.setRemoteAddress("147.120.1.5"); - request.addHeader("X-FORWARDED-FOR", "231.23.45.65"); - - assertEquals("231.23.45.65", ServletHelper.retrieveRemoteAddress(request)); - } - - @Test - public void testRetrieveRemoteAddressNoProxy() { - MockRequest request = new MockRequest(); - request.setRemoteAddress("231.23.45.65"); - - assertEquals("231.23.45.65", ServletHelper.retrieveRemoteAddress(request)); - } - - private final static class MockRequest extends MockHttpServletRequest { - - private String remoteAddress; - - private Map<String, String> headers = new HashMap<>(); - - public void setRemoteAddress(String remoteAddress) { - this.remoteAddress = remoteAddress; - } - - public void addHeader(String name, String value) { - headers.put(name, value); - } - - @Override - public String getHeader(String name) { - return headers.get(name); - } - - @Override - public String getRemoteAddr() { - return remoteAddress; - } - - } - -} diff --git a/src/test/java/org/traccar/helper/WebHelperTest.java b/src/test/java/org/traccar/helper/WebHelperTest.java new file mode 100644 index 000000000..da18be11e --- /dev/null +++ b/src/test/java/org/traccar/helper/WebHelperTest.java @@ -0,0 +1,39 @@ +package org.traccar.helper; + +import org.junit.jupiter.api.Test; + +import jakarta.servlet.http.HttpServletRequest; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class WebHelperTest { + + @Test + public void testRetrieveRemoteAddressProxyMultiple() { + HttpServletRequest request = mock(HttpServletRequest.class); + when(request.getRemoteAddr()).thenReturn("147.120.1.5"); + when(request.getHeader("X-FORWARDED-FOR")).thenReturn("231.23.45.65, 10.20.10.33, 10.20.20.34"); + + assertEquals("231.23.45.65", WebHelper.retrieveRemoteAddress(request)); + } + + @Test + public void testRetrieveRemoteAddressProxySingle() { + HttpServletRequest request = mock(HttpServletRequest.class); + when(request.getRemoteAddr()).thenReturn("147.120.1.5"); + when(request.getHeader("X-FORWARDED-FOR")).thenReturn("231.23.45.65"); + + assertEquals("231.23.45.65", WebHelper.retrieveRemoteAddress(request)); + } + + @Test + public void testRetrieveRemoteAddressNoProxy() { + HttpServletRequest request = mock(HttpServletRequest.class); + when(request.getRemoteAddr()).thenReturn("231.23.45.65"); + + assertEquals("231.23.45.65", WebHelper.retrieveRemoteAddress(request)); + } + +} diff --git a/src/test/java/org/traccar/notification/NotificiationMailTest.java b/src/test/java/org/traccar/notification/NotificiationMailTest.java index b82bec02e..ccc8cc47d 100644 --- a/src/test/java/org/traccar/notification/NotificiationMailTest.java +++ b/src/test/java/org/traccar/notification/NotificiationMailTest.java @@ -1,13 +1,13 @@ package org.traccar.notification; -import org.junit.Ignore; -import org.junit.Test; - -import javax.mail.Message; -import javax.mail.Session; -import javax.mail.Transport; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeMessage; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import jakarta.mail.Message; +import jakarta.mail.Session; +import jakarta.mail.Transport; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeMessage; import java.util.Properties; public class NotificiationMailTest { @@ -25,7 +25,7 @@ public class NotificiationMailTest { private static final int PORT = 25; - @Ignore + @Disabled @Test public void test() throws Exception { diff --git a/src/test/java/org/traccar/protocol/AdmFrameDecoderTest.java b/src/test/java/org/traccar/protocol/AdmFrameDecoderTest.java index 1682ca56d..bd50e1d14 100644 --- a/src/test/java/org/traccar/protocol/AdmFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AdmFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AdmFrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class AdmFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new AdmFrameDecoder(); + var decoder = inject(new AdmFrameDecoder()); verifyFrame( binary("38363931353330343235323337383400003728e000001402441d5f42c3711642930d000000c7000a461954f25fd82ed508000000000000000044000000010000000000140000"), diff --git a/src/test/java/org/traccar/protocol/AdmProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AdmProtocolDecoderTest.java index 7c8769925..f09a42ba8 100644 --- a/src/test/java/org/traccar/protocol/AdmProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AdmProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AdmProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class AdmProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new AdmProtocolDecoder(null); + var decoder = inject(new AdmProtocolDecoder(null)); verifyPosition(decoder, binary( "38363931353330343235323337383400003728e000001402441d5f42c3711642930d000000c7000a461954f25fd82ed508000000000000000044000000010000000000140000")); diff --git a/src/test/java/org/traccar/protocol/AdmProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/AdmProtocolEncoderTest.java index 89ddef849..26f59015d 100644 --- a/src/test/java/org/traccar/protocol/AdmProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/AdmProtocolEncoderTest.java @@ -16,18 +16,18 @@ */ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class AdmProtocolEncoderTest extends ProtocolTest { @Test public void testEncode() throws Exception { - var encoder = new AdmProtocolEncoder(null); + var encoder = inject(new AdmProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/AisProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AisProtocolDecoderTest.java index f869ab8f3..b322bbbc7 100644 --- a/src/test/java/org/traccar/protocol/AisProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AisProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AisProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class AisProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new AisProtocolDecoder(null); + var decoder = inject(new AisProtocolDecoder(null)); verifyPositions(decoder, text( "!AIVDM,2,1,8,A,53UlSb01l>Ei=H4KF218PTpv222222222222221?8h=766gB0<Ck11DTp888,0*14s:MTb827ebc7686b,c:1481688227737*4d\\\r\n" + diff --git a/src/test/java/org/traccar/protocol/AlematicsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AlematicsProtocolDecoderTest.java index 8bd457a97..84108160a 100644 --- a/src/test/java/org/traccar/protocol/AlematicsProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AlematicsProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AlematicsProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class AlematicsProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new AlematicsProtocolDecoder(null); + var decoder = inject(new AlematicsProtocolDecoder(null)); verifyPosition(decoder, text( "$T,2,64,866050035975497,20180726103446,20180726103514,23.033305,72.558032,0,0,41,5.4,4,0,0,0.000,12.960,0,")); diff --git a/src/test/java/org/traccar/protocol/AnytrekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AnytrekProtocolDecoderTest.java index b6f95513c..aec26c0c1 100644 --- a/src/test/java/org/traccar/protocol/AnytrekProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AnytrekProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AnytrekProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class AnytrekProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new AnytrekProtocolDecoder(null); + var decoder = inject(new AnytrekProtocolDecoder(null)); verifyPosition(decoder, binary( "78783500300086428703204121160085015111050C0A0D20C6FD24A102FF8EAC0C01001404000000FFFFFFFF131702210000000000000000000D0A")); diff --git a/src/test/java/org/traccar/protocol/ApelProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ApelProtocolDecoderTest.java index b922d3ef1..6aef8409b 100644 --- a/src/test/java/org/traccar/protocol/ApelProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ApelProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ApelProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class ApelProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new ApelProtocolDecoder(null); + var decoder = inject(new ApelProtocolDecoder(null)); /*byte[] buf1 = {0x40,0x4E,0x54,0x43,0x01,0x00,0x00,0x00,0x7B,0x00,0x00,0x00,0x13,0x00,0x44,0x34,0x2A,0x3E,0x53,0x3A,0x38,0x36,0x31,0x37,0x38,0x35,0x30,0x30,0x35,0x32,0x30,0x35,0x30,0x37,0x39}; verifyNull(decoder, text( ChannelBuffers.wrappedBuffer(ByteOrder.LITTLE_ENDIAN, buf1)));*/ diff --git a/src/test/java/org/traccar/protocol/AplicomFrameDecoderTest.java b/src/test/java/org/traccar/protocol/AplicomFrameDecoderTest.java index 96a219d2a..316e897a7 100644 --- a/src/test/java/org/traccar/protocol/AplicomFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AplicomFrameDecoderTest.java @@ -1,16 +1,16 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class AplicomFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new AplicomFrameDecoder(); + var decoder = inject(new AplicomFrameDecoder()); assertEquals( binary("44C20146B710C158DA009500B09F7700C054CA0EA454CA0EA403BE0BF6015D706B070000142A600000000000000002434946010801000754CA0EA4000000000000008400000000000000000000000000000000300000FE00FE0000000000000000000000000000000000000000000000000000000000000000000040502035000000000000020D0000030D0000040C0000040D0000050C0000050D0000058C0000060C"), diff --git a/src/test/java/org/traccar/protocol/AplicomProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AplicomProtocolDecoderTest.java index 5abe9550d..b4b5da0ce 100644 --- a/src/test/java/org/traccar/protocol/AplicomProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AplicomProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AplicomProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class AplicomProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new AplicomProtocolDecoder(null); + var decoder = inject(new AplicomProtocolDecoder(null)); verifyNull(decoder, binary( "434946010A0100075253F85F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEEA0000FEE90000F0030000F0040000FEF10000FEF20000FEF50000FEFC0000FEC10000FEE500")); diff --git a/src/test/java/org/traccar/protocol/AppelloProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AppelloProtocolDecoderTest.java index da4d82938..d0dd368e7 100644 --- a/src/test/java/org/traccar/protocol/AppelloProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AppelloProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AppelloProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class AppelloProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new AppelloProtocolDecoder(null); + var decoder = inject(new AppelloProtocolDecoder(null)); verifyAttributes(decoder, text( "FOLLOWIT,867273024233699,UTCTIME,0.000000,0.000000,0,0,0,0,L,262:3:c703:4bf8:64:255|262:3:c703:9a18:45|262:3:c703:e838:33|262:3:c703:7190:20|262:3:c704:d896:17|,02,44,,31,,4.20,0,0,86/44/24,,,,26,02264DFF6E16:69|4C09D408554E:79|4C09D408554F:79|E0885DE705E5:81|E2885DE705E7:81|246511122CCC:83|,34925")); diff --git a/src/test/java/org/traccar/protocol/AquilaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AquilaProtocolDecoderTest.java index 57d793c71..fa9cc9851 100644 --- a/src/test/java/org/traccar/protocol/AquilaProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AquilaProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AquilaProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class AquilaProtocolDecoderTest extends ProtocolTest { @Test public void testDecodeA() throws Exception { - var decoder = new AquilaProtocolDecoder(null); + var decoder = inject(new AquilaProtocolDecoder(null)); verifyPosition(decoder, text( "$$CLIENT_1ZF,170215089,20,18.462809,73.824188,170613182744,A,01,123456,*37")); diff --git a/src/test/java/org/traccar/protocol/Ardi01ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Ardi01ProtocolDecoderTest.java index 653e4346d..9f567c87b 100644 --- a/src/test/java/org/traccar/protocol/Ardi01ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Ardi01ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Ardi01ProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class Ardi01ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Ardi01ProtocolDecoder(null); + var decoder = inject(new Ardi01ProtocolDecoder(null)); verifyPosition(decoder, text( "013227003054776,20141010052719,24.4736042,56.8445807,110,289,40,7,5,78,-1"), diff --git a/src/test/java/org/traccar/protocol/ArknavProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ArknavProtocolDecoderTest.java index b95963f40..e3a2658a8 100644 --- a/src/test/java/org/traccar/protocol/ArknavProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ArknavProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ArknavProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class ArknavProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new ArknavProtocolDecoder(null); + var decoder = inject(new ArknavProtocolDecoder(null)); verifyPosition(decoder, text( "358266016278447,05*827,000,L001,V,4821.6584,N,01053.8650,E,000.0,000.0,00.0,08:46:04 17-03-16,9.5A,D7,0,79,0,,,,"), diff --git a/src/test/java/org/traccar/protocol/ArknavX8ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ArknavX8ProtocolDecoderTest.java index c2d78dca6..c81ef1684 100644 --- a/src/test/java/org/traccar/protocol/ArknavX8ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ArknavX8ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ArknavX8ProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class ArknavX8ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new ArknavX8ProtocolDecoder(null); + var decoder = inject(new ArknavX8ProtocolDecoder(null)); verifyNull(decoder, text( "351856045213782,241111")); diff --git a/src/test/java/org/traccar/protocol/ArmoliProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ArmoliProtocolDecoderTest.java index da2542b34..42e1ab477 100644 --- a/src/test/java/org/traccar/protocol/ArmoliProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ArmoliProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,7 @@ public class ArmoliProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new ArmoliProtocolDecoder(null); + var decoder = inject(new ArmoliProtocolDecoder(null)); verifyAttribute(decoder, text( "[M869867039550712160821153237N41.033508E029.2697032F00036000000410006B336FFFFG458563@A6D>04410C2482>03410F56>03412F19>0441210000>034130FF>0441313A7>03410D30>04411F01B6>0341048C>04410C1C98];"), diff --git a/src/test/java/org/traccar/protocol/ArnaviBinaryProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ArnaviBinaryProtocolDecoderTest.java index f2940de59..e38e63755 100644 --- a/src/test/java/org/traccar/protocol/ArnaviBinaryProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ArnaviBinaryProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ArnaviBinaryProtocolDecoderTest extends ProtocolTest { @@ -8,9 +8,7 @@ public class ArnaviBinaryProtocolDecoderTest extends ProtocolTest { @Test public void testHeader1Decode() throws Exception { - ArnaviBinaryProtocolDecoder decoder; - - decoder = new ArnaviBinaryProtocolDecoder(null); + var decoder = inject(new ArnaviBinaryProtocolDecoder(null)); verifyNull(decoder, binary( "ff22f30c45f5c90f0300")); @@ -23,9 +21,7 @@ public class ArnaviBinaryProtocolDecoderTest extends ProtocolTest { @Test public void testHeader2Decode() throws Exception { - ArnaviBinaryProtocolDecoder decoder; - - decoder = new ArnaviBinaryProtocolDecoder(null); + var decoder = inject(new ArnaviBinaryProtocolDecoder(null)); verifyNull(decoder, binary( "ff23f30c45f5c90f0300")); diff --git a/src/test/java/org/traccar/protocol/ArnaviFrameDecoderTest.java b/src/test/java/org/traccar/protocol/ArnaviFrameDecoderTest.java index 3f495731a..4997e535d 100644 --- a/src/test/java/org/traccar/protocol/ArnaviFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ArnaviFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ArnaviFrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class ArnaviFrameDecoderTest extends ProtocolTest { @Test public void testDecodeValidPackets() throws Exception { - var decoder = new ArnaviFrameDecoder(); + var decoder = inject(new ArnaviFrameDecoder()); verifyFrame( binary("2441562c563344492c38353136342c3231342c2d312c31392c30303030344634462c30303030303935452c30433030303030322c3836333037313031333034313631382c38393939373031353630333832353236363232462c2a3039"), diff --git a/src/test/java/org/traccar/protocol/ArnaviTextProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ArnaviTextProtocolDecoderTest.java index 9e9047be4..ed9fd0cfe 100644 --- a/src/test/java/org/traccar/protocol/ArnaviTextProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ArnaviTextProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ArnaviTextProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class ArnaviTextProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new ArnaviTextProtocolDecoder(null); + var decoder = inject(new ArnaviTextProtocolDecoder(null)); verifyPosition(decoder, buffer( "$AV,V4,999999,12487,2277,203,65534,0,0,193,65535,65535,65535,65535,1,13,80.0,56.1,200741,5950.6773N,03029.1043E,300.0,360.0,121012,65535,65535,65535,SF*6E")); diff --git a/src/test/java/org/traccar/protocol/AstraProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AstraProtocolDecoderTest.java index 3376fa3f0..3dabcac5d 100644 --- a/src/test/java/org/traccar/protocol/AstraProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AstraProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AstraProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class AstraProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new AstraProtocolDecoder(null); + var decoder = inject(new AstraProtocolDecoder(null)); verifyPositions(decoder, binary( "4b00700529c0c265976b8202cba9ff00676d864554a9c30000000020073401006436000300030008000000000000a0000100001920c43d00009600428302cba9ff00676d864554aa3e000000002007240100643b000300020008000000000000b0000100001920c43d00009600420f0e")); diff --git a/src/test/java/org/traccar/protocol/At2000FrameDecoderTest.java b/src/test/java/org/traccar/protocol/At2000FrameDecoderTest.java index 0782c2d34..c83a5fd4e 100644 --- a/src/test/java/org/traccar/protocol/At2000FrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/At2000FrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class At2000FrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class At2000FrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new At2000FrameDecoder(); + var decoder = inject(new At2000FrameDecoder()); verifyFrame( binary("01012f00000000000000000000000000003335363137333036343430373439320fad981997ae8e031fe10c0ea7641903ca32c0331df467233d2a9cd886fbeef8"), diff --git a/src/test/java/org/traccar/protocol/At2000ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/At2000ProtocolDecoderTest.java index 9e2f180d2..718542e97 100644 --- a/src/test/java/org/traccar/protocol/At2000ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/At2000ProtocolDecoderTest.java @@ -1,9 +1,9 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assume.assumeTrue; +import static org.junit.jupiter.api.Assumptions.assumeTrue; public class At2000ProtocolDecoderTest extends ProtocolTest { @@ -14,7 +14,7 @@ public class At2000ProtocolDecoderTest extends ProtocolTest { At2000ProtocolDecoder decoder; - decoder = new At2000ProtocolDecoder(null); + decoder = inject(new At2000ProtocolDecoder(null)); verifyNull(decoder, binary( "01012f00000000000000000000000000003335373435343037313632373539388b57ec3a6ec7e3310a1ceb0a70fd751b8f2e7be6df1d6dcd80129f66fff0ea1c")); @@ -22,7 +22,7 @@ public class At2000ProtocolDecoderTest extends ProtocolTest { verifyNull(decoder, binary( "89000000000000000000000000000000")); - decoder = new At2000ProtocolDecoder(null); + decoder = inject(new At2000ProtocolDecoder(null)); verifyNull(decoder, binary( "01012f0000000000000000000000000000333537343534303731363036313936ddf189075add9a32d97b54073025963e65849a3a59940d05fd8db655fc84bc6d")); @@ -30,7 +30,7 @@ public class At2000ProtocolDecoderTest extends ProtocolTest { verifyPositions(decoder, binary( "893b01000000000000000000000000003b40bcdab6387d829146701d8cb53daaa87b84d24b40cb24fd86da5d3f5f02b0f6f603c43c5a513418a0b2bdcaba603dbe737687cfe9082c57668eb6789d2b029a35aeac6a609558b96de5d7ad37917c902efc878ca9aff474f9d5d2417191285b8d5749bd3ffa86cc99096ce24c1f6ac350ae9adf3d5c788f80b4e3d3dc2dbb8abc1414ea1b52fdb55b2bb8af223ec528245f99d451b715e5774c5397db645d9ae441e645f8dae70230b728e81f51240868712d6f426fd694dbad8026fcf487c268939f04593ad86391cc829b1a1bdac8804ff7507544a69dc0b1b3927d7344e8a5b26fa56825283b3e476330b36d15011e1647ebd9f2ef71844ed32c0dc050457bfbd79160e6d1d8cda00a0927c8957631770e98eb20735aa46b0b18502baf4c45d2623ee51a4320cf3018010e7bbf8bc0dd79eb28e88b727ea67e980b8a91")); - decoder = new At2000ProtocolDecoder(null); + decoder = inject(new At2000ProtocolDecoder(null)); verifyNull(decoder, binary( "01012f000000000000000000000000000033353734353430373136303631393637f5441a9862260117858237fe3160388490f0df7d46c09112ee087235a92101")); @@ -38,7 +38,7 @@ public class At2000ProtocolDecoderTest extends ProtocolTest { verifyPositions(decoder, binary( "89043203000000000000000000000000d01ff1df1b56ba9185bb741ddc253f3cee093b1f8193100cd95777b5288a6f29d1b343a952f882ce8825679f7e27ad88ed7898bff92f716acadfc3c17fa8c1a6b9d0934e8f042433a183776c06c1acd73efb4b9f19030debb4dceb161fb3e6630757d25c3e995b7cf5f446318dcc1677eb215d1af49f11cd7300598bcdc40cc25466ed2391d836c782e44bc04a332e902b2b34f5597a542af4ca670cdfc18d87ce2a225c3e6f2f32359d4914c6df09aa5ee306c229260d4a56da53f93398bc8a6e77095305ee214cf605de20d3876a993fb810486f75bcd514c12442bf4dc3fbe7963b20d5100b5ecff1c1aef4c4b3736a04e245d50f538327db21d55270b279db5ac5a9658876bae3d9b5026b8975bb2bf4d100b8492760d66ae31f27bf9c525c2d794860eabca9c788b91152dbce79f336daaf6a7a9547bf1dd8e3334c891f4548fd6d112ebf45125c2a8abd3a786ebbcfdd03101b524bbf465f14a9a424305ce7de56ffca85b4657fc8c03e4349c0ca6be64d1cf595ee91f8173678ef2267dae54dd00028450c48d9b74c925af0f245d409d8773238dce5832747587f53a12155869c1d464eb0630f94cf8dceb76aa39995411d4ce7743b1501692425afec498535526067e79f568b7f71ee47d8b4929118d57b13d56cdbfd26582d579dee")); - decoder = new At2000ProtocolDecoder(null); + decoder = inject(new At2000ProtocolDecoder(null)); verifyNull(decoder, binary( "01012f0000000000000000000000000000333537343534303731363035353033dd529a1eb5df9f3b6d320b38250e03306692957e8c2127d8e381a717f639b4c9")); @@ -46,7 +46,7 @@ public class At2000ProtocolDecoderTest extends ProtocolTest { verifyPositions(decoder, binary( "89898701000000000000000000000000ae99e38f13d44f536769eb4930a6826dfebe5b98a6048941e89b17c9cdcb276be4af7c0d188d07c90d6e94aa9efcb465fe7aeaff4d85caf837483b4e9b32fbbacfbc4e175eebf57a27f552a64fc3419565d2dfbea668511a08d5a526342fad0e93b20c4449ad8defab4a9ac68cf7dad86971eb2cd96810d9d6a9c56e07fd90e4c28cfc53a069b63efe37a0523a69b607a2dc011ba17b177c5332c04be1faeeabed24539b3b790fa8a8610ab3633e0140ed79690fcae9dea43c7daad780d95a511d8f4875e621bcfe7516a03b80eb3c473ffd4bc1eda298dfa7d994a2cfeaa5d24c190d52d72fd90975a2e6f9ed3b95017133952262f91787c46839738a80c333dc53ee4d8afe75315d801efe17bc7309f30cfce64906bf70e6844c835781cbb64b49e9315ca3c2cd39d00a03cc7178a4ebc5df230dcdfd44ec588791d488f96bb6ff4007a753f552bda4d1766632aa3ec5eb38feb23ed6efb8f382a7f22b70adc9cb533c09bf749190c36d63b572c1acfc3a59138d51273835ab13c4689df01e3d2c2dd1829e00aac5c56b5d51e60d6731833f82c7464d88df663ca28a20eedeecb60f3704ae78281838caa116184e414db459768321bbfa1e83ad59fe168eb81f3b41cfe0e39c8aa78cbbe5825620bf053a1cb62e04d4cdf17ca2dc9305d47c")); - decoder = new At2000ProtocolDecoder(null); + decoder = inject(new At2000ProtocolDecoder(null)); verifyNull(decoder, binary( "01012f00000000000000000000000000003335363137333036343430373439320fad981997ae8e031fe10c0ea7641903ca32c0331df467233d2a9cd886fbeef8")); diff --git a/src/test/java/org/traccar/protocol/AtrackFrameDecoderTest.java b/src/test/java/org/traccar/protocol/AtrackFrameDecoderTest.java index 77e90ca53..7b2334919 100644 --- a/src/test/java/org/traccar/protocol/AtrackFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AtrackFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AtrackFrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class AtrackFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new AtrackFrameDecoder(); + var decoder = inject(new AtrackFrameDecoder()); verifyFrame( binary("4052698c032a924f000147027fe5d7425f642e56060f031847bb68cb500719e26752c25bebc11c7fddce2b8ed4eff4ed863b187cc6653b5b1c1fc6803884d21aeeedae2ec6e72781d97e95b965610c1d107e5400cd5a7b7b3b592e676091c6a5893d80af9b3c63ae4de20d6e5bc60440bf2c299fbabfe268039d558e4b8589dd5173c926b7f51b916ba29f21d46ff9170793fe450072d691896e114fddce4fd29f7f2f9b74e41ca83814015e8a00ffd1f9bd475e2a44624e074a009455ab5628e39fce8036a09368cf1d2ba0d2653b979c0a00e9edc82335a56d1ee6071401d468b0f4cd761a743d011401d15b4636015721870dd0500695b2edabeabf2f4a00a514645cc83a739ad165f320c1ed401617a0a2800a2803ffd2faa68a004660aa598e00acd8f4d866d54eab3c7994284881fe11ebf5e68034e8a0086e674850927f0ae2bc4dafa5844659674451d39e49a00e1dae23d76ed67bb72211d109e4d5bd756da3b68a4b755021e30076a00cf31431a064e41e6a19a68d5396518f7a00f1ff008bfe27f31068766dbb7e1a723d3fbbf8d79aeb764748b489662be7ccbbb6820e07d4500734caa727765aa32ac0720e28026b4bb9ed7ccf2594798bb5b2a0f1f8f4a82800a2803fffd3f9b97352ae02e45004c808e4f7a997823bd005e86600618f26b7b4a9cab819fa500767a749b9403cd74162b903de803acd1e3c28aebf4d4c81401d05a4441fad682444738a00b712f2055f03e502802b14c5dffbc2ac106343ed4012a905411e94b40051401fffd4faa6992488980c793d0773400d54676df2f1e8b9e054b400564ebb77750c463b442d2119247f08f53e9401e7da85d6a12cd221d427217a856c60fe15caea9689292f2832bfac8777f3a00e67538ef150ff00665d9b4b95fba4aee46f623fa8ae26fbe24f88b49b87d3b5bd2a12e38ca3950e3d41e7228008be2ac02d423dadc09071c1047e791fcab96d77c79acdf92969279113f1c1cb7e7401876c4c939b8ba73230e5998e49ac4d66ee6bebd796462ddb9f4ed40140a12339e9dea225b1824d0025140051401fffd5f9bc676f6a7ae4af6e280255cf5c7153a7b0a0052d8715bba64bf32f39a00ed74694902bb1d306e65c500763a5afca2baed2"), diff --git a/src/test/java/org/traccar/protocol/AtrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AtrackProtocolDecoderTest.java index 4a66dbf58..be6fb5c45 100644 --- a/src/test/java/org/traccar/protocol/AtrackProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AtrackProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AtrackProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class AtrackProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new AtrackProtocolDecoder(null); + var decoder = inject(new AtrackProtocolDecoder(null)); verifyNull(decoder, binary( "4052698c032a924f000147027fe5d7425f642e56060f031847bb68cb500719e26752c25bebc11c7fddce2b8ed4eff4ed863b187cc6653b5b1c1fc6803884d21aeeedae2ec6e72781d97e95b965610c1d107e5400cd5a7b7b3b592e676091c6a5893d80af9b3c63ae4de20d6e5bc60440bf2c299fbabfe268039d558e4b8589dd5173c926b7f51b916ba29f21d46ff9170793fe450072d691896e114fddce4fd29f7f2f9b74e41ca83814015e8a00ffd1f9bd475e2a44624e074a009455ab5628e39fce8036a09368cf1d2ba0d2653b979c0a00e9edc82335a56d1ee6071401d468b0f4cd761a743d011401d15b4636015721870dd0500695b2edabeabf2f4a00a514645cc83a739ad165f320c1ed401617a0a2800a2803ffd2faa68a004660aa598e00acd8f4d866d54eab3c7994284881fe11ebf5e68034e8a0086e674850927f0ae2bc4dafa5844659674451d39e49a00e1dae23d76ed67bb72211d109e4d5bd756da3b68a4b755021e30076a00cf31431a064e41e6a19a68d5396518f7a00f1ff008bfe27f31068766dbb7e1a723d3fbbf8d79aeb764748b489662be7ccbbb6820e07d4500734caa727765aa32ac0720e28026b4bb9ed7ccf2594798bb5b2a0f1f8f4a82800a2803fffd3f9b97352ae02e45004c808e4f7a997823bd005e86600618f26b7b4a9cab819fa500767a749b9403cd74162b903de803acd1e3c28aebf4d4c81401d05a4441fad682444738a00b712f2055f03e502802b14c5dffbc2ac106343ed4012a905411e94b40051401fffd4faa6992488980c793d0773400d54676df2f1e8b9e054b400564ebb77750c463b442d2119247f08f53e9401e7da85d6a12cd221d427217a856c60fe15caea9689292f2832bfac8777f3a00e67538ef150ff00665d9b4b95fba4aee46f623fa8ae26fbe24f88b49b87d3b5bd2a12e38ca3950e3d41e7228008be2ac02d423dadc09071c1047e791fcab96d77c79acdf92969279113f1c1cb7e7401876c4c939b8ba73230e5998e49ac4d66ee6bebd796462ddb9f4ed40140a12339e9dea225b1824d0025140051401fffd5f9bc676f6a7ae4af6e280255cf5c7153a7b0a0052d8715bba64bf32f39a00ed74694902bb1d306e65c500763a5afca2baed2")); @@ -43,6 +43,12 @@ public class AtrackProtocolDecoderTest extends ProtocolTest { decoder.setCustom(true); + verifyPositions(decoder, binary( + "4050d78502e01d29000312fa45441d6d647d8e67647d8e67647eef190205437c021846e6001a020002d7f000070000000000000007d007d000254349254d5625525025564e254d4625454c25545225455425464c254d4c25464325534d25494e3025484125484225484325415400010c0000000000000000000000000000000000000000000000000e647d8e85647d8e86647eef190205437c021846e60019020002d7f000070000000000000007d007d000254349254d5625525025564e254d4625454c25545225455425464c254d4c25464325534d25494e3025484125484225484325415400010c0000000000000000000000000000000000000000000000000e647d8ea3647d8ea4647eef190205437c021846e60019020002d7f000070000000000000007d007d000254349254d5625525025564e254d4625454c25545225455425464c254d4c25464325534d25494e3025484125484225484325415400010b0000000000000000000000000000000000000000000000000e647d8ec1647d8ec2647eef190205437c021846e60019020002d7f000070000000000000007d007d000254349254d5625525025564e254d4625454c25545225455425464c254d4c25464325534d25494e3025484125484225484325415400010a0000000000000000000000000000000000000000000000000e647d8edf647d8ee0647eef190205437c021846e60019020002d7f000070000000000000007d007d000254349254d5625525025564e254d4625454c25545225455425464c254d4c25464325534d25494e3025484125484225484325415400010a0000000000000000000000000000000000000000000000000e647d8efd647d8efe647eef190205437c021846e60019020002d7f000070000000000000007d007d000254349254d5625525025564e254d4625454c25545225455425464c254d4c25464325534d25494e3025484125484225484325415400010a0000000000000000000000000000000000000000000000000e")); + + verifyPositions(decoder, binary( + "405099280272000300014399e3f93d136438abdf644083f56440842afb2711c701b9eaee0067020003e0bb03de0000000000000007d007d00025434925454c25455425464325464c255250254d4c25534d25545225494125454f25564e254d56254256254548255a4c33255a4f3134255a4f3131255a4f3130255a4f32255a4c3400000000000000000000000000000000000000000000930025000000000000000000000000000000006438abdf644083f76440842afb2711c701b9eaee0067710003e0bb03de0100000000000007d007d00025434925454c25455425464325464c255250254d4c25534d25545225494125454f25564e254d56254256254548255a4c33255a4f3134255a4f3131255a4f3130255a4f32255a4c3400000000000000000000000000000000000000000000950025000000000000000000000000000000006438abdf644083f76440842afb2711c701b9eaee0067840003e0bb03de0100000000000007d007d00025434925454c25455425464325464c255250254d4c25534d25545225494125454f25564e254d56254256254548255a4c33255a4f3134255a4f3131255a4f3130255a4f32255a4c3400000000000000000000000000000000000000000000950025000000000000000000000000000000006438abdf644083f86440842afb2711c701b9eaee0067760003e0bb03de0100000000000007d007d00025434925454c25455425464325464c255250254d4c25534d25545225494125454f25564e254d56254256254548255a4c33255a4f3134255a4f3131255a4f3130255a4f32255a4c340000000000000000000000000000000000000000000095002500000000000000000000000000000000")); + verifyPositions(decoder, buffer( "@P,7A66,153,9022,863003048505515,20210207000103,20210207000103,20210207000103,103939276,1348903,97,2,5628,8,0,0,0,0,,2000,2000,\u001a,%CI%BC,3:224:F128833445E6002C09C6\r\n")); @@ -111,7 +117,7 @@ public class AtrackProtocolDecoderTest extends ProtocolTest { @Test public void testDecodeCustom() throws Exception { - var decoder = new AtrackProtocolDecoder(null); + var decoder = inject(new AtrackProtocolDecoder(null)); decoder.setCustom(true); diff --git a/src/test/java/org/traccar/protocol/AuroProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AuroProtocolDecoderTest.java index fbe3ad0a3..d2ef74c02 100644 --- a/src/test/java/org/traccar/protocol/AuroProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AuroProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AuroProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class AuroProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new AuroProtocolDecoder(null); + var decoder = inject(new AuroProtocolDecoder(null)); verifyPosition(decoder, text( "M0028T0000816398975I357325031465123E00001W*****110620150437000068DA#RD01DA240000000001+100408425+013756121100620152137231112240330004400")); diff --git a/src/test/java/org/traccar/protocol/AustinNbProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AustinNbProtocolDecoderTest.java index 0be22b333..f46fe6a3c 100644 --- a/src/test/java/org/traccar/protocol/AustinNbProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AustinNbProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AustinNbProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class AustinNbProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new AustinNbProtocolDecoder(null); + var decoder = inject(new AustinNbProtocolDecoder(null)); verifyPosition(decoder, text( "48666666666;2017-01-01 16:31:01;52,1133308410645;21,1000003814697;310;120;2292;1;ORANGE")); diff --git a/src/test/java/org/traccar/protocol/AutoFonProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AutoFonProtocolDecoderTest.java index 3e64defdb..f0c9c17bd 100644 --- a/src/test/java/org/traccar/protocol/AutoFonProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AutoFonProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; @@ -9,7 +9,7 @@ public class AutoFonProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new AutoFonProtocolDecoder(null); + var decoder = inject(new AutoFonProtocolDecoder(null)); verifyNull(decoder, binary( "10556103592310314825728F")); diff --git a/src/test/java/org/traccar/protocol/AutoGradeProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AutoGradeProtocolDecoderTest.java index 697ac8a06..be4b38a14 100644 --- a/src/test/java/org/traccar/protocol/AutoGradeProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AutoGradeProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AutoGradeProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class AutoGradeProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new AutoGradeProtocolDecoder(null); + var decoder = inject(new AutoGradeProtocolDecoder(null)); verifyPosition(decoder, text( "(000000001637868324027912356171116A2250.7611N07556.9425E000.9024427197.36\u008eA0000B0000C0000D0000E0000K0000L0000M0000N0000O0000)")); diff --git a/src/test/java/org/traccar/protocol/AutoTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AutoTrackProtocolDecoderTest.java index 64a7459ce..3b2f2c9b7 100644 --- a/src/test/java/org/traccar/protocol/AutoTrackProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AutoTrackProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AutoTrackProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class AutoTrackProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new AutoTrackProtocolDecoder(null); + var decoder = inject(new AutoTrackProtocolDecoder(null)); verifyNull(decoder, binary( "f1f1f1f1330c00201007090006de7200000000daa3")); diff --git a/src/test/java/org/traccar/protocol/AvemaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AvemaProtocolDecoderTest.java index 6138711aa..7d44e1b15 100644 --- a/src/test/java/org/traccar/protocol/AvemaProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AvemaProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,7 @@ public class AvemaProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new AvemaProtocolDecoder(null); + var decoder = inject(new AvemaProtocolDecoder(null)); verifyAttribute(decoder, text( "1000000000,20190527072358,121.646024,25.062135,0,0,0,0,10,0.0,1,0.02,12.32,0,0,15,2,466-5,10275,0,0.01,65EB812A000104E0,8000001234,NormanChang"), diff --git a/src/test/java/org/traccar/protocol/Avl301ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Avl301ProtocolDecoderTest.java index fb1984d87..ada32f8ee 100644 --- a/src/test/java/org/traccar/protocol/Avl301ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Avl301ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; @@ -9,7 +9,7 @@ public class Avl301ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Avl301ProtocolDecoder(null); + var decoder = inject(new Avl301ProtocolDecoder(null)); verifyNull(decoder, binary( "244c0f086058500087335500010d0a")); diff --git a/src/test/java/org/traccar/protocol/B2316ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/B2316ProtocolDecoderTest.java index 6b9c71b0e..84b09e7b7 100644 --- a/src/test/java/org/traccar/protocol/B2316ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/B2316ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class B2316ProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class B2316ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new B2316ProtocolDecoder(null); + var decoder = inject(new B2316ProtocolDecoder(null)); verifyPositions(decoder, false, text( "{\"imei\":\"866349041783600\",\"data\":[{\"tm\":1631162952,\"wn\":7},{\"tm\":1631158729,\"ic\":\"89883030000059398609\",\"ve\":\"B2316.TAU.U.TH01\"},{\"tm\":1631158805,\"te\":\"312,363\",\"st\":0,\"ba\":3,\"sn\":80},{\"tm\":1631158829,\"ci\":\"505,1,8218,133179149,-108\"},{\"tm\":1631162956,\"wi\":\"101331c17f4f,-74;f46bef7953bb,-81;b09575cff1c8,-86;e2b9e5d61a7a,-88;b0ee7b4dee2f,-88;e0b9e5d61a77,-89;f66bef7953b9,-89;\",\"te\":\"335,366\",\"hr\":58,\"bp\":\"113,73\",\"st\":0,\"ba\":3,\"sn\":60},{\"tm\":1631162968,\"ci\":\"505,1,8218,133179149,-105\"}]}")); diff --git a/src/test/java/org/traccar/protocol/BceFrameDecoderTest.java b/src/test/java/org/traccar/protocol/BceFrameDecoderTest.java new file mode 100644 index 000000000..f25daca76 --- /dev/null +++ b/src/test/java/org/traccar/protocol/BceFrameDecoderTest.java @@ -0,0 +1,26 @@ +package org.traccar.protocol; + +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; + +public class BceFrameDecoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + var decoder = inject(new BceFrameDecoder()); + + verifyNull( + decoder.decode(null, null, binary("23424345230d0a"))); + + verifyFrame( + binary("18ed450cf31403001501a5050a66207bde6442534451380a66207bde6440534451380a66207bde6414534451380a66207bde6416534451380a96207bde6415534451386247277bde03c0ffffc081400069f934418ce94b42001c88ee0000000000908c060103025d19ab004b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000017b5c40000000000000001010000000aa6277bde008f5344513862b7277bde03c0ffffc081400051f8344185e94b420c1c85fa0000000000d18c060103025d19ab00470000000000000000000000000000000000000000000000000000000000000000000000000000000000000017b5c40000000000000001010400000ab6277bde009153445138a0"), + decoder.decode(null, null, binary("18ed450cf31403001501a5050a66207bde6442534451380a66207bde6440534451380a66207bde6414534451380a66207bde6416534451380a96207bde6415534451386247277bde03c0ffffc081400069f934418ce94b42001c88ee0000000000908c060103025d19ab004b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000017b5c40000000000000001010000000aa6277bde008f5344513862b7277bde03c0ffffc081400051f8344185e94b420c1c85fa0000000000d18c060103025d19ab00470000000000000000000000000000000000000000000000000000000000000000000000000000000000000017b5c40000000000000001010400000ab6277bde009153445138a0"))); + + verifyFrame( + binary("18ed450cf31403000200a5070e"), + decoder.decode(null, null, binary("18ed450cf31403000200a5070e"))); + + } + +} diff --git a/src/test/java/org/traccar/protocol/BceProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/BceProtocolDecoderTest.java index 544d49967..fdfb721c5 100644 --- a/src/test/java/org/traccar/protocol/BceProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/BceProtocolDecoderTest.java @@ -1,15 +1,28 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; public class BceProtocolDecoderTest extends ProtocolTest { + @Disabled + @Test + public void testDecodeFail() throws Exception { + + var decoder = inject(new BceProtocolDecoder(null)); + + // Needs to be fixed + verifyPositions(decoder, binary( + "18ed450cf3140300c800a53a62972f7bde03c0ffffc0814000e03e354135e34b42121c55fb0000000000d18c060103025d19ab00540000000000000000000000000000000000000000000000000000000000000000000000000000000000000017b5c400000000000000010104080162a72f7bde03c0ffffc0814000ef3e8e4431e34b42061c54fc0000000000d18c060103025d19ab00540000000000000000000000000000000000000000000000000000000000000000000000000000000000000017b5c400000000000000010100000053")); + + } + @Test public void testDecode() throws Exception { - var decoder = new BceProtocolDecoder(null); + var decoder = inject(new BceProtocolDecoder(null)); verifyNull(decoder, binary( "3ab90b71bc1503000300c10bff11")); diff --git a/src/test/java/org/traccar/protocol/BceProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/BceProtocolEncoderTest.java index c72012e5a..93c05fad6 100644 --- a/src/test/java/org/traccar/protocol/BceProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/BceProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; @@ -9,7 +9,7 @@ public class BceProtocolEncoderTest extends ProtocolTest { @Test public void testEncode() throws Exception { - var encoder = new BceProtocolEncoder(null); + var encoder = inject(new BceProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/BlackKiteProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/BlackKiteProtocolDecoderTest.java index 3fdac8479..7ec070615 100644 --- a/src/test/java/org/traccar/protocol/BlackKiteProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/BlackKiteProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class BlackKiteProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class BlackKiteProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new BlackKiteProtocolDecoder(null); + var decoder = inject(new BlackKiteProtocolDecoder(null)); verifyNull(decoder, binary( "01150003313131313131313131313131313131209836055605BA")); diff --git a/src/test/java/org/traccar/protocol/BlueProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/BlueProtocolDecoderTest.java index 4e74adf38..33496fada 100644 --- a/src/test/java/org/traccar/protocol/BlueProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/BlueProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,7 @@ public class BlueProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new BlueProtocolDecoder(null); + var decoder = inject(new BlueProtocolDecoder(null)); verifyAttribute(decoder, binary( "AA0056860080E3E79E0C811F80000114020207170520011F00407F8005EE1938113B270000000000000000140202071705005AC7A621121F0002000100B7000080110000000000001A3A0000000001F400000000000078"), diff --git a/src/test/java/org/traccar/protocol/BoxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/BoxProtocolDecoderTest.java index 5b639e12b..d2af8282a 100644 --- a/src/test/java/org/traccar/protocol/BoxProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/BoxProtocolDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class BoxProtocolDecoderTest extends ProtocolTest { @@ -9,7 +9,7 @@ public class BoxProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new BoxProtocolDecoder(null); + var decoder = inject(new BoxProtocolDecoder(null)); verifyNull(decoder, text( "H,BT,358281002435893,081028142432,F5813D19,6D6E6DC2")); diff --git a/src/test/java/org/traccar/protocol/BstplProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/BstplProtocolDecoderTest.java new file mode 100644 index 000000000..fd3561d18 --- /dev/null +++ b/src/test/java/org/traccar/protocol/BstplProtocolDecoderTest.java @@ -0,0 +1,21 @@ +package org.traccar.protocol; + +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; + +public class BstplProtocolDecoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + var decoder = inject(new BstplProtocolDecoder(null)); + + verifyPosition(decoder, text( + "BSTPL$1,869630054439504,V,200722,045113,00.000000,0,00.00000,0,0,0,000,00,0,17,1,1,0,0,00.01,0,04.19,15B_190821,8991000907387031196F,12.27")); + + verifyPosition(decoder, text( + "BSTPL$1,AP12AP3456,A,130720,160552,27.244183,N,83.673973,E,20,156,183,17,0,11,1,0,0,0,00.00,00,04.16,15_V1_0_0,89917380578146790443,12.16")); + + } + +} diff --git a/src/test/java/org/traccar/protocol/C2stekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/C2stekProtocolDecoderTest.java index 6fb14b902..0dc6529e1 100644 --- a/src/test/java/org/traccar/protocol/C2stekProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/C2stekProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class C2stekProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,10 @@ public class C2stekProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new C2stekProtocolDecoder(null); + var decoder = inject(new C2stekProtocolDecoder(null)); + + verifyPosition(decoder, text( + "PA$012207006145046$D#190607#123157#1#37.947087#023.768669#000.00#314.6#00000.0#4104#000#8$AP")); verifyPosition(decoder, text( "PA$867965024889327$D#220222#135059#0#+37.98995#+23.85141#0.00#69.2#0.0#0000#000#8#00#sz-w1001#B2600$AP")); diff --git a/src/test/java/org/traccar/protocol/CalAmpProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CalAmpProtocolDecoderTest.java index 79d27e2ab..932facaee 100644 --- a/src/test/java/org/traccar/protocol/CalAmpProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/CalAmpProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class CalAmpProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class CalAmpProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new CalAmpProtocolDecoder(null); + var decoder = inject(new CalAmpProtocolDecoder(null)); verifyPosition(decoder, binary( "830547643586340101010400105f9c39ba5f9c302eeb36d5bddf39df700000000000000000003400040321ff9f4f0087080200001c30783330304544383946333335303139303030303030343637450d0a")); diff --git a/src/test/java/org/traccar/protocol/CarTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CarTrackProtocolDecoderTest.java index dd96c2585..e5791c706 100644 --- a/src/test/java/org/traccar/protocol/CarTrackProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/CarTrackProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class CarTrackProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class CarTrackProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new CarTrackProtocolDecoder(null); + var decoder = inject(new CarTrackProtocolDecoder(null)); verifyNull(decoder, text( "$$020040????????&A0000")); diff --git a/src/test/java/org/traccar/protocol/CarscopProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CarscopProtocolDecoderTest.java index b1fe69bac..d5e60e69c 100644 --- a/src/test/java/org/traccar/protocol/CarscopProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/CarscopProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class CarscopProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class CarscopProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new CarscopProtocolDecoder(null); + var decoder = inject(new CarscopProtocolDecoder(null)); verifyNull(decoder, text( "*170821223045UB00HSO")); diff --git a/src/test/java/org/traccar/protocol/CastelProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CastelProtocolDecoderTest.java index f90fbe5ba..52c6a86a6 100644 --- a/src/test/java/org/traccar/protocol/CastelProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/CastelProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,15 @@ public class CastelProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new CastelProtocolDecoder(null); + var decoder = inject(new CastelProtocolDecoder(null)); + + verifyAttribute(decoder, binary( + "40404700043231335732303139303033353400000000000000400BBE723A5DEF723A5D000000000000000000000000000000000000030100011900030001012603030145C90D0A"), + Position.KEY_DTCS, "P0326"); + + verifyAttribute(decoder, binary( + "40404500033231334c323031373030303432320000000000004006e1ad205bf1ad205b48510f000000000050160000000000020400053f007c000083040001511346160d0a"), + Position.KEY_DTCS, "P1351"); verifyAttribute(decoder, binary( "40403a00043231334744503230313830323133343300000000a002000001000001012011004d414c43333831434d4b4d353637313438c8fc0d0a"), diff --git a/src/test/java/org/traccar/protocol/CastelProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/CastelProtocolEncoderTest.java index cdf6a7c3b..e3e59b7e7 100644 --- a/src/test/java/org/traccar/protocol/CastelProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/CastelProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; @@ -9,7 +9,7 @@ public class CastelProtocolEncoderTest extends ProtocolTest { @Test public void testEncode() throws Exception { - var encoder = new CastelProtocolEncoder(null); + var encoder = inject(new CastelProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/CautelaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CautelaProtocolDecoderTest.java index fe8586068..dd8b812fa 100644 --- a/src/test/java/org/traccar/protocol/CautelaProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/CautelaProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class CautelaProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class CautelaProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new CautelaProtocolDecoder(null); + var decoder = inject(new CautelaProtocolDecoder(null)); verifyPosition(decoder, text( "20,010907000000,14,02,18,16.816667,96.166667,1325,S,*2E")); diff --git a/src/test/java/org/traccar/protocol/CellocatorFrameDecoderTest.java b/src/test/java/org/traccar/protocol/CellocatorFrameDecoderTest.java index 04a0f74d5..aae9b434d 100644 --- a/src/test/java/org/traccar/protocol/CellocatorFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/CellocatorFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class CellocatorFrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class CellocatorFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new CellocatorFrameDecoder(); + var decoder = inject(new CellocatorFrameDecoder()); verifyFrame( binary("4D4347500BA9880B00C80A7E003800000000000806000001210A14000613000000000D4457A5F71442AC02E80300000100040707000011171408060E1C08000100000200020000FD"), diff --git a/src/test/java/org/traccar/protocol/CellocatorProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CellocatorProtocolDecoderTest.java index 3ae5350f2..01dd9f4cb 100644 --- a/src/test/java/org/traccar/protocol/CellocatorProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/CellocatorProtocolDecoderTest.java @@ -1,14 +1,19 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; +import org.traccar.model.Position; public class CellocatorProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new CellocatorProtocolDecoder(null); + var decoder = inject(new CellocatorProtocolDecoder(null)); + + verifyAttribute(decoder, binary( + "4d4347500098ab31000856b12b2c041016002c0023b3000021f3f5ffb04c8f0100000000000078dd0004020f716445f75f3b0701126e0200b303000036002538151b0ce607ab"), + Position.KEY_IGNITION, true); verifyPosition(decoder, binary( "4D43475000856308000004B2DE1F04009E00200100000000696CF7AB002F1A00000000000000325C000402069BFDE70857E22502F41C000036000000DF0B0932100B09DC0719")); diff --git a/src/test/java/org/traccar/protocol/CellocatorProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/CellocatorProtocolEncoderTest.java index 9d42bd65d..4d5c8cfe4 100644 --- a/src/test/java/org/traccar/protocol/CellocatorProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/CellocatorProtocolEncoderTest.java @@ -1,17 +1,17 @@ package org.traccar.protocol; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; public class CellocatorProtocolEncoderTest extends ProtocolTest { - @Ignore + @Disabled @Test public void testEncode() throws Exception { - var encoder = new CellocatorProtocolEncoder(null); + var encoder = inject(new CellocatorProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/CguardProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CguardProtocolDecoderTest.java index a7ed0e754..df5141e44 100644 --- a/src/test/java/org/traccar/protocol/CguardProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/CguardProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class CguardProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class CguardProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new CguardProtocolDecoder(null); + var decoder = inject(new CguardProtocolDecoder(null)); verifyNull(decoder, text( "IDRO:354868050655283")); diff --git a/src/test/java/org/traccar/protocol/CityeasyProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CityeasyProtocolDecoderTest.java index 07d6368ab..1f99e02e7 100644 --- a/src/test/java/org/traccar/protocol/CityeasyProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/CityeasyProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; @@ -9,7 +9,7 @@ public class CityeasyProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new CityeasyProtocolDecoder(null); + var decoder = inject(new CityeasyProtocolDecoder(null)); verifyNotNull(decoder, binary( "545400853575570249020100033b3430342c34352c31303638312c31313632312c33352c31303638312c31313632322c32332c31303638312c32383938332c32332c31303638312c31313632332c32312c31303638312c32333338312c31372c31303638312c32323538332c31372c31303638312c32363434312c31330000000d352e0d0a")); diff --git a/src/test/java/org/traccar/protocol/CityeasyProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/CityeasyProtocolEncoderTest.java index 2722f6474..0b97ed9c8 100644 --- a/src/test/java/org/traccar/protocol/CityeasyProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/CityeasyProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; @@ -9,7 +9,7 @@ public class CityeasyProtocolEncoderTest extends ProtocolTest { @Test public void testEncode() throws Exception { - var encoder = new CityeasyProtocolEncoder(null); + var encoder = inject(new CityeasyProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/ContinentalProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ContinentalProtocolDecoderTest.java index 2924d2c4d..659c5aa9c 100644 --- a/src/test/java/org/traccar/protocol/ContinentalProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ContinentalProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ContinentalProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class ContinentalProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new ContinentalProtocolDecoder(null); + var decoder = inject(new ContinentalProtocolDecoder(null)); verifyPosition(decoder, binary( "5356003216001eb48505025b4001e90f7f18ce0f00522200400001015b4001e9000e820100000c24000100014e0400736a7a"), diff --git a/src/test/java/org/traccar/protocol/CradlepointProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CradlepointProtocolDecoderTest.java index 1e3023b39..6e3ce2481 100644 --- a/src/test/java/org/traccar/protocol/CradlepointProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/CradlepointProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class CradlepointProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class CradlepointProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new CradlepointProtocolDecoder(null); + var decoder = inject(new CradlepointProtocolDecoder(null)); verifyPosition(decoder, text( "356526070063940,0,4337.19009,N,11612.34705,W,0.0,277.2,AT&T,,,-79,,-14.0,")); diff --git a/src/test/java/org/traccar/protocol/DingtekFrameDecoderTest.java b/src/test/java/org/traccar/protocol/DingtekFrameDecoderTest.java index 6236410b3..6219b3da4 100644 --- a/src/test/java/org/traccar/protocol/DingtekFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/DingtekFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class DingtekFrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class DingtekFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new DingtekFrameDecoder(); + var decoder = inject(new DingtekFrameDecoder()); verifyFrame( binary("383030303031303131453032383830303138303030303030303030313545303038303545433430303031313836383832323034303130343433303831"), diff --git a/src/test/java/org/traccar/protocol/DingtekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DingtekProtocolDecoderTest.java index 8b3d07c22..3131ee1df 100644 --- a/src/test/java/org/traccar/protocol/DingtekProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/DingtekProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class DingtekProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class DingtekProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new DingtekProtocolDecoder(null); + var decoder = inject(new DingtekProtocolDecoder(null)); verifyPosition(decoder, text( "800001011e0692001a00000000016e008027c40000186962703655111781")); diff --git a/src/test/java/org/traccar/protocol/DishaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DishaProtocolDecoderTest.java index 4492b3ed0..a9b80664c 100644 --- a/src/test/java/org/traccar/protocol/DishaProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/DishaProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class DishaProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class DishaProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new DishaProtocolDecoder(null); + var decoder = inject(new DishaProtocolDecoder(null)); verifyPosition(decoder, text( "$A#A#864161028848856#A#053523#010216#2232.7733#N#08821.1940#E#002.75#038.1#09#00.8#1800#0#000#0000#9999#11.7#285.7#0001*")); diff --git a/src/test/java/org/traccar/protocol/DmtHttpProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DmtHttpProtocolDecoderTest.java index 31d0efa12..c2449ea91 100644 --- a/src/test/java/org/traccar/protocol/DmtHttpProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/DmtHttpProtocolDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; import io.netty.handler.codec.http.HttpMethod; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class DmtHttpProtocolDecoderTest extends ProtocolTest { @@ -9,7 +9,7 @@ public class DmtHttpProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new DmtHttpProtocolDecoder(null); + var decoder = inject(new DmtHttpProtocolDecoder(null)); verifyAttributes(decoder, request(HttpMethod.POST, "/", buffer("{\"date\":\"2021-12-12T16:04:52Z\",\"device\":{\"sn\":\"416581\",\"prod\":85,\"rev\":1,\"fw\":\"1.12\",\"iccid\":\"89011702278612797427\",\"imei\":\"351358810439486\"},\"sqn\":1549,\"reason\":42,\"counters\":[{\"id\":0,\"val\":5304},{\"id\":3,\"val\":3200},{\"id\":4,\"val\":5066},{\"id\":128,\"val\":1},{\"id\":129,\"val\":8},{\"id\":130,\"val\":0},{\"id\":131,\"val\":0},{\"id\":132,\"val\":0},{\"id\":134,\"val\":1},{\"id\":138,\"val\":0},{\"id\":139,\"val\":36},{\"id\":142,\"val\":1629023},{\"id\":145,\"val\":0},{\"id\":146,\"val\":1}]}"))); diff --git a/src/test/java/org/traccar/protocol/DmtProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DmtProtocolDecoderTest.java index 82a4cd5ff..6919f8ed8 100644 --- a/src/test/java/org/traccar/protocol/DmtProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/DmtProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class DmtProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class DmtProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new DmtProtocolDecoder(null); + var decoder = inject(new DmtProtocolDecoder(null)); verifyNull(decoder, binary( "0255003300001b00003335333232393032373533393235310038393931353030303030303030313330343539340000000403041910780603")); diff --git a/src/test/java/org/traccar/protocol/DolphinProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DolphinProtocolDecoderTest.java index 0215973e8..b24f30965 100644 --- a/src/test/java/org/traccar/protocol/DolphinProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/DolphinProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class DolphinProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class DolphinProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new DolphinProtocolDecoder(null); + var decoder = inject(new DolphinProtocolDecoder(null)); verifyPositions(decoder, binary( "ababff8f0300000100000100e03c0b86c70e03005c0f0000ab4cd12d0aa9010d0d7f0e4215f6fe6c421d00b0ce4420122daa9a9042359a99f9413806455e53fc5f48015a41050a0b1e29323334353637464850649601a401a601aa01ab01ad01b401b501b601b701b801c905ca05cb05cd05d105d205e605e705ef059c069e06a206a906aa0662404801010ce41bb8f4cc2ad801fe98f3030893dd9a010001019299f303155400000007b401000000000072ac01b99401f8de8a0adc03900e0049000003004bfa010aa7010d4f7f0e421505ff6c421dcd1cce4420112d3ef984423552b89e413806456053fc5f48015a41050a0b1e29323334353637464850649601a401a601aa01ab01ad01b401b501b601b701b801c905ca05cb05cd05d105d205e605e705ef059c069e06a206a906aa06623e4201010ce41bd8f4cc2a20ff98f3030193dd9a010001019399f30315420000000000000000000072ad018f950181df8a0aee03cd0d004b000003004bfa010aa7010da67f0e42154fff6c421d3393cd4420112d073c9942358fc2df413806456253fc5f48015a41050a0b1e29323334353637464850649601a401a601aa01ab01ad01b401b501b601b701b801c905ca05cb05cd05d105d205e605e705ef059c069e06a206a906aa06623e4c010111f51d88f5cc2a308199f3030293dd9a010001019599f303154c0000000000000000000072ad01ef95018bdf8a0aee03d30d004a000003004bfa010aa9010dfd800e421525006d421d3373d04420102da5b7aa4235ec51c4413806456853fc5f48015a41050a0b1e29323334353637464850649601a401a601aa01ab01ad01b401b501b601b701b801c905ca05cb05cd05d105d205e605e705ef059c069e06a206a906aa0662405501010ef01db0f6cc2aa8018899f3030793dd9a010001019c99f3031555000000068a010000000000739d01df9501acdf8a0ac0028a150031000003004bfa010aa8010d3b830e421561016d421dcddcd04420102d8edda94235cdccbe413806457353fc5f48015a41050a0b1e29323334353637464850649601a401a601aa01ab01ad01b401b501b601b701b801c905ca05cb05cd05d105d205e605e705ef059c069e06a206a906aa06623f5401010ef01db8f8cc2a88029499f3030c93dd9a01000101a899f303155500000004480000000000728a018f9501e5df8a0aa9018d280015000003004bfa010aa8010ddd850e4215b3026d421dcd3cd04420112d2e9ba942359a99af413806458053fc5f48015a41050a0b1e29323334353637464850649601a401a601aa01ab01ad01b401b501b601b701b801c905ca05cb05cd05d105d205e605e705ef059c069e06a206a906aa06623f5401010df01df0facc2ab802a199f3030d93dd9a01000101b599f303155500000005760000000000728b01f59401a2e08a0a9b01c12b0018000003004bfa010aa9010d4a870e421554036d421d66d6cf4420112d5f29a842355c8faa413806458753fc5f48015a41050a0b1e29323334353637464850649601a401a601aa01ab01ad01b401b501b601b701b801c905ca05cb05cd05d105d205e605e705ef059c069e06a206a906aa0662405401010dea1d98fccc2aa801a899f3030793dd9a01000101bc99f303155400000007a4010000000000719701b29401bae08a0a9202be180026000003004bfa010aa7010da7870e421591036d421d33d3cf4420112d6329a942353333a7413806458953fc5f48015a41050a0b1e29323334353637464850649601a401a601aa01ab01ad01b401b501b601b701b801c905ca05cb05cd05d105d205e605e705ef059c069e06a206a906aa06623e5401010df51dc8fccc2a30aa99f3030293dd9a01000101be99f3031554000000022c0000000000719901db9401c3e08a0aae02a016002b000003004bfa010aa7010d1a880e4215b6036d421d33e3cf4420112ddc45aa423514aea3413806458b53fc5f48015a41050a0b1e29323334353637464850649601a401a601aa01ab01ad01b401b501b601b701b801c905ca05cb05cd05d105d205e605e705ef059c069e06a206a906aa06623e5501010df51df8fccc2a30ac99f3030293dd9a01000101c099f303155500000002320000000000719e01f69401d1e08a0a9f02b3170031000003004bfa010aa8010d50890e42154d046d421d3363d04420102da0b7a94235d7a3a2413806459253fc5f48015a41050a0b1e29323334353637464850649601a401a601aa01ab01ad01b401b501b601b701b801c905ca05cb05cd05d105d205e605e705ef059c069e06a206a906aa06623f5401010edf1d88fecc2a9001b399f3030793dd9a01000101c799f303155400000000000000000000729a01e69501f7e08a0ab0028e16002d000003004bfa010aa9010d188c0e421573056d421d00c0ce4420102d300c84423514ae7741380645a053fc5f48015a41050a0b1e29323334353637464850649601a401a601aa01ab01ad01b401b501b601b701b801c905ca05cb05cd05d105d205e605e705ef059c069e06a206a906aa06624042010110da1dd080cd2ac802c199f3030e93dd9a01000101d599f303155500000009e2010000000000729801b19501afe18a0a97029018002a000003004bfa010aa7010d528c0e42159d056d421d3313cf4420102dfec98c423552b8c241380645a053fc5f48015a41050a0b1e29323334353637464850649601a401a601aa01ab01ad01b401b501b601b701b801c905ca05cb05cd05d105d205e605e705ef059c069e06a206a906aa06623e46010110da1df080cd2a20c299f3030193dd9a01000101d699f303154600000000000000000000729701df9501b8e18a0aa002b9170029000003004bfa010aa9010d198f0e42159e066d421d9a49d04420112dfae6a642359a999941380645ad53fc5f48015a41050a0b1e29323334353637464850649601a401a601aa01ab01ad01b401b501b601b701b801c905ca05cb05cd05d105d205e605e705ef059c069e06a206a906aa06624053010111e51da883cd2ab802cf99f3030d93dd9a01000101e399f30315540000000ca4020000000000729601c49501f1e18a0a9102dc180028000003004bfa010aa7010da68f0e4215e9066d421d9a19d044200f2d83dda74235ec519441380645b153fc5f48015a41050a0b1e29323334353637464850649601a401a601aa01ab01ad01b401b501b601b701b801c905ca05cb05cd05d105d205e605e705ef059c069e06a206a906aa06623e53010111e51df083cd2a48d299f3030393dd9a01000101e699f303155300000003400000000000729701de950183e28a0a8e02ff180028000003004bfa010aa7010d40900e42151a076d421d33c3cf4420102d717e9b423548e15e41380645b453fc5f48015a41050a0b1e29323334353637464850649601a401a601aa01ab01ad01b401b501b601b701b801c905ca05cb05cd05d105d205e605e705ef059c069e06a206a906aa06623e4d010110e51db884cd2a48d599f3030393dd9a01000101e999f303154d00000003420000000000729801bf950191e28a0a9a02f117002a000003004bfa010aa7010d5f900e42153c076d421d66c6cf44200f2de3028d4235713daa41380645b553fc5f48015a41050a0b1e29323334353637464850649601a401a601aa01ab01ad01b401b501b601b701b801c905ca05cb05cd05d105d205e605e705ef059c069e06a206a906aa06623e46010110da1dc884cd2a10d699f3030193dd9a01000101ea99f303154600000001120000000000729901bf950191e28a0a98029318002b000003004bfa010aa7010dae900e42153d076d421d9ad9cf44200f2d324fa342357b144241380645b653fc5f48015a41050a0b1e29323334353637464850649601a401a601aa01ab01ad01b401b501b601b701b801c905ca05cb05cd05d105d205e605e705ef059c069e06a206a906aa06623e51010110da1de884cd2a20d799f3030193dd9a01000101eb99f303155100000000000000000000729b01a5950196e28a0aa2029b17002c000003004bfa010aa7010d08910e42157e076d421d33b3cf4420102d51b79a42357b14b241380645b853fc5f48015a41050a0b1e29323334353637464850649601a401a601aa01ab01ad01b401b501b601b701b801c905ca05cb05cd05d105d205e605e705ef059c069e06a206a906aa06623e4d010110da1d9885cd2a30d999f3030293dd9a01000101ed99f3031552000000022c0000000000729b01ca95019be28a0ab602e815002f000003004bfa010aa9010d3b930e42153f086d421d9a99d044200f2d5a3ca9423552b88441380645c153fc5f48015a41050a0b1e29323334353637464850649601a401a601aa01ab01ad01b401b501b601b701b801c905ca05cb05cd05d105d205e605e705ef059c069e06a206a906aa06624054010110f51d9087cd2af801e499f3030b93dd9a01000101f899f30315540000000bfa010000000000729901cf9501cee28a0ac502f814002e000003004bfa010aa9010df1950e421558096d421d6646d24420102da3a4a842357b149441380645cf53fc5f48015a41050a0b1e29323334353637464850649601a401a601aa01ab01ad01b401b501b601b701b801c905ca05cb05cd05d105d205e605e705ef059c069e06a206a906aa0662405401010eea1dc889cd2ab802f199f3030d93dd9a01000101859af303155500000008be010000000000729901c7950190e38a0a9c02da17002c000003004bfa010aa9010dcf970e4215260a6d421d6676d34420112d653cab423552b89e41380645d853fc5f48015a41050a0b1e29323334353637464850649601a401a601aa01ab01ad01b401b501b601b701b801c905ca05cb05cd05d105d205e605e705ef059c069e06a206a906aa0662405501010efa1ba08bcd2ad801fa99f3030993dd9a010001018e9af303155600000007a8010000000000729e01bb9501bae38a0ac00284150031000003004bfa010aa8010de89a0e4215510b6d421d00a0d44420112d1aaea74235cdcc6841380645e653fc5f48015a41050a0b1e29323334353637464850649601a401a601aa01ab01ad01b401b501b601b701b801c905ca05cb05cd05d105d205e605e705ef059c069e06a206a906aa06623f53010110f51d808ecd2ae002899af3030f93dd9a010001019d9af30315550000000460000000000073a401e59501fce38a0a9003e510003e000003004bfa010aa9010d6b9c0e4215b00b6d421d00b0d44420112dd46bac423585eb0541380645ed53fc5f48015a41050a0b1e29323334353637464850649601a401a601aa01ab01ad01b401b501b601b701b801c905ca05cb05cd05d105d205e605e705ef059c069e06a206a906aa06624056010110ea1da88fcd2aa801919af3030893dd9a01000101a59af303155600000008a801000000000073a201a19501a1e48a0a8103ce110038000003004bfa01")); diff --git a/src/test/java/org/traccar/protocol/Dsf22FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Dsf22FrameDecoderTest.java index fc18b0560..b0c90ef7c 100644 --- a/src/test/java/org/traccar/protocol/Dsf22FrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Dsf22FrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Dsf22FrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class Dsf22FrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Dsf22FrameDecoder(); + var decoder = inject(new Dsf22FrameDecoder()); verifyFrame( binary("4642000101A8EE5F0ECA5FF421B33F524E32610401"), diff --git a/src/test/java/org/traccar/protocol/Dsf22ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Dsf22ProtocolDecoderTest.java index 4089c208c..dbf6f782c 100644 --- a/src/test/java/org/traccar/protocol/Dsf22ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Dsf22ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Dsf22ProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class Dsf22ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Dsf22ProtocolDecoder(null); + var decoder = inject(new Dsf22ProtocolDecoder(null)); verifyPositions(decoder, binary( "4642a82d01c8f6aa1af1792c0c1411eb61001e0000")); diff --git a/src/test/java/org/traccar/protocol/DualcamFrameDecoderTest.java b/src/test/java/org/traccar/protocol/DualcamFrameDecoderTest.java index 46f32a8ae..495e20e8e 100644 --- a/src/test/java/org/traccar/protocol/DualcamFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/DualcamFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class DualcamFrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class DualcamFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new DualcamFrameDecoder(); + var decoder = inject(new DualcamFrameDecoder()); verifyFrame( binary("000000050001403a4abaa31444000400"), diff --git a/src/test/java/org/traccar/protocol/DualcamProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DualcamProtocolDecoderTest.java index 3dd11bdf7..de70e2b63 100644 --- a/src/test/java/org/traccar/protocol/DualcamProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/DualcamProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class DualcamProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class DualcamProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new DualcamProtocolDecoder(null); + var decoder = inject(new DualcamProtocolDecoder(null)); verifyNull(decoder, binary( "000000050001403a4abaa31444000400")); diff --git a/src/test/java/org/traccar/protocol/DwayProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DwayProtocolDecoderTest.java index 368f8b4ac..05b2757ad 100644 --- a/src/test/java/org/traccar/protocol/DwayProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/DwayProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class DwayProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class DwayProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new DwayProtocolDecoder(null); + var decoder = inject(new DwayProtocolDecoder(null)); verifyPosition(decoder, text( "AA55,36,10024,1,171025,161055,36.0294,-79.7881,201, 2.5,111,1000,0000,00000,3578,0,0,0,D")); diff --git a/src/test/java/org/traccar/protocol/EasyTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EasyTrackProtocolDecoderTest.java index 5fda7fb28..bcd9aea45 100644 --- a/src/test/java/org/traccar/protocol/EasyTrackProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/EasyTrackProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class EasyTrackProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class EasyTrackProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new EasyTrackProtocolDecoder(null); + var decoder = inject(new EasyTrackProtocolDecoder(null)); verifyNotNull(decoder, text( "*ET,354522180593498,JZ,0,20222,262,724,4#")); diff --git a/src/test/java/org/traccar/protocol/EasyTrackProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/EasyTrackProtocolEncoderTest.java index 79b4e66e3..a2a720263 100644 --- a/src/test/java/org/traccar/protocol/EasyTrackProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/EasyTrackProtocolEncoderTest.java @@ -1,17 +1,17 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class EasyTrackProtocolEncoderTest extends ProtocolTest { @Test - public void testEncodeEngineStop() { + public void testEncodeEngineStop() throws Exception { - var encoder = new EasyTrackProtocolEncoder(null); + var encoder = inject(new EasyTrackProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java index c1cc3c39a..6e2c41a7f 100644 --- a/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,7 @@ public class EelinkProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new EelinkProtocolDecoder(null); + var decoder = inject(new EelinkProtocolDecoder(null)); verifyPositions(decoder, binary( "454c029249a50354679090044671676712004321315f3cf43503fc94d3760c79328a0129000000000a01f9000190330905580d2e046f118a04ec00000000ccc7086c02fe000000000000000000000000000000000000676712004321325f3cf43e03fc94d3760c79328a0129000000000901f9000190330905580d2e046f117b04ec00000000ccc7086d02ff000000000000000000000000000000000000676712004321335f3cf44703fc94d3760c79328a0129000000000901f9000190330905580d2e046f117f04ec00000000ccc7086d02ff000000000000000000000000000000000000676712004321345f3cf45303fc94d3760c79328a0129000000000901f9000190330905580d2e046f119d04ec00000000ccc7086d02ff000000000000000000000000000000000000676712004321355f3cf45c03fc94d3760c79328a0129000000000801f9000190330905580d2e046f11a304ec00000000ccc7086d02ff000000000000000000000000000000000000676712004321365f3cf46603fc94d3760c79328a0129000000000801f9000190330905580d2e046f118804df00000000ccc7086d02ff000000000000000000000000000000000000676712004321375f3cf47103fc94d3760c79328a0129000000000901f9000190330905580d2e046f119704ec00000000ccc7086d02ff000000000000000000000000000000000000676712004321385f3cf47a03fc94d3760c79328a0129000000000901f9000190330905580d2e046f118204ec00000000ccc7086e0300000000000000000000000000000000000000676712004321395f3cf48303fc94d3760c79328a0129000000000901f9000190330905580d2e046f117604df00000000ccc7086e0300000000000000000000000000000000000000")); diff --git a/src/test/java/org/traccar/protocol/EelinkProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/EelinkProtocolEncoderTest.java index 616ca0b52..300a1c5b6 100644 --- a/src/test/java/org/traccar/protocol/EelinkProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/EelinkProtocolEncoderTest.java @@ -1,21 +1,34 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; public class EelinkProtocolEncoderTest extends ProtocolTest { @Test - public void testEncode() throws Exception { + public void testEncodeTcp() throws Exception { + + var encoder = inject(new EelinkProtocolEncoder(null, false)); Command command = new Command(); command.setDeviceId(1); command.setType(Command.TYPE_ENGINE_STOP); - verifyCommand(new EelinkProtocolEncoder(null, false), command, binary("676780000f0000010000000052454c41592c3123")); + verifyCommand(encoder, command, binary("676780000f0000010000000052454c41592c3123")); + + } + + @Test + public void testEncodeUdp() throws Exception { + + var encoder = inject(new EelinkProtocolEncoder(null, true)); + + Command command = new Command(); + command.setDeviceId(1); + command.setType(Command.TYPE_ENGINE_STOP); - verifyCommand(new EelinkProtocolEncoder(null, true), command, binary("454c001eb41a0123456789012345676780000f0000010000000052454c41592c3123")); + verifyCommand(encoder, command, binary("454c001eb41a0123456789012345676780000f0000010000000052454c41592c3123")); } diff --git a/src/test/java/org/traccar/protocol/EgtsFrameDecoderTest.java b/src/test/java/org/traccar/protocol/EgtsFrameDecoderTest.java index a1acb1e9d..af5e77116 100644 --- a/src/test/java/org/traccar/protocol/EgtsFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/EgtsFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class EgtsFrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class EgtsFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new EgtsFrameDecoder(); + var decoder = inject(new EgtsFrameDecoder()); verifyFrame( binary("0100020B0025003A5701C91A003A5701CD6E68490202101700CBB4740F7617FD924364104F116A0000000000010300001EC2"), diff --git a/src/test/java/org/traccar/protocol/EgtsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EgtsProtocolDecoderTest.java index dcf70bcae..aa0d666b1 100644 --- a/src/test/java/org/traccar/protocol/EgtsProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/EgtsProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class EgtsProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class EgtsProtocolDecoderTest extends ProtocolTest { @Test public void testDecodeWithObjectId() throws Exception { - var decoder = new EgtsProtocolDecoder(null); + var decoder = inject(new EgtsProtocolDecoder(null)); verifyNull(decoder, binary( "0100020b002300020001871c00020000010105190000ab0800006247396e615734366347467a63336476636d513daadf")); @@ -31,7 +31,7 @@ public class EgtsProtocolDecoderTest extends ProtocolTest { @Test public void testDecodeWithAuth() throws Exception { - var decoder = new EgtsProtocolDecoder(null); + var decoder = inject(new EgtsProtocolDecoder(null)); verifyNull(decoder, binary( "0100010b002200c06401f21700c1640171360d00010101140071360d000238363539303500000000000000000047fc")); diff --git a/src/test/java/org/traccar/protocol/EnforaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EnforaProtocolDecoderTest.java index 14e982e39..c53d65ceb 100644 --- a/src/test/java/org/traccar/protocol/EnforaProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/EnforaProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class EnforaProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class EnforaProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new EnforaProtocolDecoder(null); + var decoder = inject(new EnforaProtocolDecoder(null)); verifyNull(decoder, binary( "000A08002020202020303131303730303030353730323637")); diff --git a/src/test/java/org/traccar/protocol/EnnfuProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EnnfuProtocolDecoderTest.java index 4afa5921f..5f1d152f6 100644 --- a/src/test/java/org/traccar/protocol/EnnfuProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/EnnfuProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class EnnfuProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class EnnfuProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new EnnfuProtocolDecoder(null); + var decoder = inject(new EnnfuProtocolDecoder(null)); verifyPosition(decoder, text( "Ennfu:354679095321652,041504.00,A,3154.86654,N,11849.08737,E,0.053,,080121,20,3.72,21.4,V0.01")); diff --git a/src/test/java/org/traccar/protocol/EnvotechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EnvotechProtocolDecoderTest.java index 6a455975d..68a3ee9e3 100644 --- a/src/test/java/org/traccar/protocol/EnvotechProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/EnvotechProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class EnvotechProtocolDecoderTest extends ProtocolTest { @@ -8,10 +8,17 @@ public class EnvotechProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new EnvotechProtocolDecoder(null); + var decoder = inject(new EnvotechProtocolDecoder(null)); verifyPosition(decoder, text( - "$80SLM,02,F,AB0010,130410155921,431750216,000040,0000,,00000000,'13041015592110476673N10111459E001281*2A")); + "$80IVM,03,E002215,E002215,110422061936,672763902,126423,0180,000000,00018600,0.0000'11042206193710406325S03966094E000118*42D6#")); + + verifyPosition(decoder, text( + "$80SLM,62,000F,F015727,020522053200,611000000,000391,C080,000,80028900,85010000'02052205320110399426S03970217E000145*E0C2#")); + + verifyPosition(decoder, text( + "$80SLM,02,F,AB0010,130410155921,431750216,000040,0000,,00000000,'13041015592110476673N10111459E001281*2A"), + position("2010-04-13 15:59:21.000", true, 4.76673, 101.11459)); verifyPosition(decoder, text( "$80SLM,82,F,AB0010,130410155921,431750216,000040,0000,,00000000,'13041015592110476673N10111459E001281@B0,F,C456,038,00,M234567,,,1A2A3A4A5A6A*4E")); diff --git a/src/test/java/org/traccar/protocol/EsealProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EsealProtocolDecoderTest.java index d0d9e3c41..4cccff878 100644 --- a/src/test/java/org/traccar/protocol/EsealProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/EsealProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class EsealProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class EsealProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new EsealProtocolDecoder(null); + var decoder = inject(new EsealProtocolDecoder(null)); verifyPosition(decoder, text( "##S,eSeal,1000821,256,3.0.6,Normal,34,2017-08-31,08:14:40,15,A,25.708828N 100.372870W,10,0,Close,0.71,0:0:3:0,3.8,-73,E##")); diff --git a/src/test/java/org/traccar/protocol/EsealProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/EsealProtocolEncoderTest.java index 88be3ebc9..e87a56b67 100644 --- a/src/test/java/org/traccar/protocol/EsealProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/EsealProtocolEncoderTest.java @@ -1,17 +1,17 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class EsealProtocolEncoderTest extends ProtocolTest { @Test public void testEncode() throws Exception { - var encoder = new EsealProtocolEncoder(null); + var encoder = inject(new EsealProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/EskyFrameDecoderTest.java b/src/test/java/org/traccar/protocol/EskyFrameDecoderTest.java index c3926c8d7..830ee4df0 100644 --- a/src/test/java/org/traccar/protocol/EskyFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/EskyFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class EskyFrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class EskyFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new EskyFrameDecoder(); + var decoder = inject(new EskyFrameDecoder()); verifyNull( decoder.decode(null, null, binary("00"))); diff --git a/src/test/java/org/traccar/protocol/EskyProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EskyProtocolDecoderTest.java index ace55f934..feec3ebfb 100644 --- a/src/test/java/org/traccar/protocol/EskyProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/EskyProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,7 @@ public class EskyProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new EskyProtocolDecoder(null); + var decoder = inject(new EskyProtocolDecoder(null)); verifyAttribute(decoder, text( "ET;0;860337031066546;R;9+200717114059+41.32053+19.80761+0.30+0+0x2+8+40381744+0+1409+11"), diff --git a/src/test/java/org/traccar/protocol/ExtremTracProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ExtremTracProtocolDecoderTest.java index 29d11e02f..ccd7d4d20 100644 --- a/src/test/java/org/traccar/protocol/ExtremTracProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ExtremTracProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ExtremTracProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class ExtremTracProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new ExtremTracProtocolDecoder(null); + var decoder = inject(new ExtremTracProtocolDecoder(null)); verifyPosition(decoder, text( "$GPRMC,862106020628733,050859.000,A,1404.8573,N,08710.9967,W,0.00,0,080117,0,,00C8,00218,99,,,,,,0.00")); diff --git a/src/test/java/org/traccar/protocol/FifotrackFrameDecoderTest.java b/src/test/java/org/traccar/protocol/FifotrackFrameDecoderTest.java index 32657b277..de320778a 100644 --- a/src/test/java/org/traccar/protocol/FifotrackFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/FifotrackFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class FifotrackFrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class FifotrackFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new FifotrackFrameDecoder(); + var decoder = inject(new FifotrackFrameDecoder()); verifyFrame( binary("24243132362c3836393436373034393239303738372c324138432c4130312c2c3139303431333135333235342c412c2d31352e3132373836382c33392e3236323530362c302c3136322c3431352c38303937323234332c302c303030302c30302c302c3634337c337c353141467c424632462c3141337c3446367c3833327c302c312c2a3534"), diff --git a/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java index 6480d1dc4..77778d885 100644 --- a/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,14 @@ public class FifotrackProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new FifotrackProtocolDecoder(null); + var decoder = inject(new FifotrackProtocolDecoder(null)); + + verifyAttributes(decoder, buffer( + "$$159,866344056951341,399D,A03,,230716222659,240|8|2724|20EEF33,4.20,100,003E,1,AE233FC0D2E0:-65|3E286D5FB6E8:-65|28BD890A4A0E:-67|8ED81B5DFC3A:-70|8AD81B5DFC3A:-70*5F")); + + verifyAttribute(decoder, buffer( + "$$99,865413050150407,7F,A03,,230626072722,460|0|25FC|AC2AB0B,3.74,52,0019,0,A,0,13,22.643466,114.018211*74"), + Position.KEY_SATELLITES, 13); verifyPosition(decoder, buffer( "$$95,866104023192332,1,A03,,210414055249,460|0|25FC|104C,4.18,100,000F,0,A,2,9,22.643175,114.018150*75")); diff --git a/src/test/java/org/traccar/protocol/FifotrackProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/FifotrackProtocolEncoderTest.java index 2da7f0842..643c9202d 100644 --- a/src/test/java/org/traccar/protocol/FifotrackProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/FifotrackProtocolEncoderTest.java @@ -1,17 +1,17 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class FifotrackProtocolEncoderTest extends ProtocolTest { @Test public void testEncode() throws Exception { - var encoder = new FifotrackProtocolEncoder(null); + var encoder = inject(new FifotrackProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/FlespiProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FlespiProtocolDecoderTest.java index 88fdb0959..47a6eab87 100644 --- a/src/test/java/org/traccar/protocol/FlespiProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/FlespiProtocolDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; import io.netty.handler.codec.http.HttpMethod; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class FlespiProtocolDecoderTest extends ProtocolTest { @@ -9,7 +9,7 @@ public class FlespiProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new FlespiProtocolDecoder(null); + var decoder = inject(new FlespiProtocolDecoder(null)); verifyPositions(decoder, request(HttpMethod.POST, "/", buffer("[{\"position.speed\":0,\"position.latitude\":53.90573,\"time.valid.status\":true,\"timestamp\":1506956075,\"position.satellites\":10,\"message.buffered.status\":false,\"business.mode.status\":true,\"gps.status\":true,\"position.longitude\":27.455848,\"position.direction\":0,\"ident\":\"605630\"},{\"siren.status\":false,\"business.mode.status\":true,\"position.satellites\":8,\"timestamp\":1506695785,\"led.status\":false,\"position.latitude\":53.905569,\"position.longitude\":27.455986,\"position.speed\":0,\"gradual.stop.status\":false,\"position.direction\":262.643854,\"hardware.version.enum\":223,\"vehicle.mileage\":160,\"message.buffered.status\":false,\"blinkers.status\":false,\"ident\":\"605630\",\"position.altitude\":233.48,\"immobilizer.status\":false}]"))); diff --git a/src/test/java/org/traccar/protocol/FlexApiProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FlexApiProtocolDecoderTest.java index c819cd8bf..bd54b4be0 100644 --- a/src/test/java/org/traccar/protocol/FlexApiProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/FlexApiProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class FlexApiProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class FlexApiProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new FlexApiProtocolDecoder(null); + var decoder = inject(new FlexApiProtocolDecoder(null)); verifyAttributes(decoder, text( "${\"topic\":\"v1/VF3102021113001/motion/info\",\"payload\":{\"motion.ts\":1641885877,\"motion.ax\":0.006344,\"motion.ay\":0.289384,\"motion.az\":-0.939156,\"motion.gx\":0.420000,\"motion.gy\":0.420000,\"motion.gz\":-0.280000}}xx")); diff --git a/src/test/java/org/traccar/protocol/FlexCommProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FlexCommProtocolDecoderTest.java index 3e988ba4f..b0b0910c6 100644 --- a/src/test/java/org/traccar/protocol/FlexCommProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/FlexCommProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class FlexCommProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class FlexCommProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new FlexCommProtocolDecoder(null); + var decoder = inject(new FlexCommProtocolDecoder(null)); verifyPosition(decoder, text( "7E00865067022408382201705302358271024932258006712785200700022601010224100040002C5002A2210001000000010012342107")); diff --git a/src/test/java/org/traccar/protocol/FlexibleReportProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FlexibleReportProtocolDecoderTest.java index 8763887a5..3bff46df0 100644 --- a/src/test/java/org/traccar/protocol/FlexibleReportProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/FlexibleReportProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class FlexibleReportProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class FlexibleReportProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new FlexibleReportProtocolDecoder(null); + var decoder = inject(new FlexibleReportProtocolDecoder(null)); verifyPosition(decoder, binary( "7d010015875000013001001028fd98991830002e7fffffff0c28fd989903f6540a07f250ed00000f02f2140f5ea20000000000000202d4000a1f8b0100000708ffff")); diff --git a/src/test/java/org/traccar/protocol/FlextrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FlextrackProtocolDecoderTest.java index 0c1c18a0c..2e0274d11 100644 --- a/src/test/java/org/traccar/protocol/FlextrackProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/FlextrackProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; @@ -9,7 +9,7 @@ public class FlextrackProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new FlextrackProtocolDecoder(null); + var decoder = inject(new FlextrackProtocolDecoder(null)); verifyNull(decoder, text( "-1,LOGON,7000000123,8945000000")); diff --git a/src/test/java/org/traccar/protocol/FoxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FoxProtocolDecoderTest.java index 439a5553a..7918243e6 100644 --- a/src/test/java/org/traccar/protocol/FoxProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/FoxProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class FoxProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class FoxProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new FoxProtocolDecoder(null); + var decoder = inject(new FoxProtocolDecoder(null)); verifyPosition(decoder, text( "<fox><gps id=\"10\" data=\"51,A,010416,085317,4444.4158,N,02025.4466,E,1,182,,1110111111110111 141 0 0 0 0 0 10010000 10142,018C81851800009B\"/></fox>")); diff --git a/src/test/java/org/traccar/protocol/FreedomProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FreedomProtocolDecoderTest.java index c8f7a444b..f8cdb8934 100644 --- a/src/test/java/org/traccar/protocol/FreedomProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/FreedomProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class FreedomProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class FreedomProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new FreedomProtocolDecoder(null); + var decoder = inject(new FreedomProtocolDecoder(null)); verifyPosition(decoder, text( "IMEI,353358011714362,2014/05/22, 20:49:32, N, Lat:4725.9624, E, Lon:01912.5483, Spd:5.05"), diff --git a/src/test/java/org/traccar/protocol/FreematicsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FreematicsProtocolDecoderTest.java index 6a7a15397..53ed07da7 100644 --- a/src/test/java/org/traccar/protocol/FreematicsProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/FreematicsProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class FreematicsProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,10 @@ public class FreematicsProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new FreematicsProtocolDecoder(null); + var decoder = inject(new FreematicsProtocolDecoder(null)); + + verifyPositions(decoder, text( + "UCFLFAYM#0:33770,24:300,82:53.000000,*F9")); verifyPositions(decoder, text( "M0ZR4X0#0:204391,11:140221,10:8445000,A:49.215920,B:18.737755,C:410,D:0,E:208,24:1252,20:0;0;0,82:47*B5")); diff --git a/src/test/java/org/traccar/protocol/FutureWayFrameDecoderTest.java b/src/test/java/org/traccar/protocol/FutureWayFrameDecoderTest.java index b8eb3908e..40d8e619e 100644 --- a/src/test/java/org/traccar/protocol/FutureWayFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/FutureWayFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class FutureWayFrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class FutureWayFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new FutureWayFrameDecoder(); + var decoder = inject(new FutureWayFrameDecoder()); verifyFrame( binary("343130303030303039424130303030344750533a562c3230303930323039333333332c302e3030303030304e2c302e303030303030452c302e3030302c302e3030300d0a574946493a332c317c39302d36372d31432d46372d32312d36437c353226327c38302d38392d31372d43362d37392d41307c353426337c34302d46342d32302d45462d44442d32417c35380d0a4c42533a3436302c302c34363437353036362c36390d0a36413432"), diff --git a/src/test/java/org/traccar/protocol/FutureWayProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FutureWayProtocolDecoderTest.java index fbb0a2aba..e8af86d66 100644 --- a/src/test/java/org/traccar/protocol/FutureWayProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/FutureWayProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class FutureWayProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class FutureWayProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new FutureWayProtocolDecoder(null); + var decoder = inject(new FutureWayProtocolDecoder(null)); verifyNull(decoder, text( "410000003F2000020,IMEI:354828100126461,battery level:6,network type:7,CSQ:236F42")); diff --git a/src/test/java/org/traccar/protocol/G1rusProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/G1rusProtocolDecoderTest.java new file mode 100644 index 000000000..059fc21df --- /dev/null +++ b/src/test/java/org/traccar/protocol/G1rusProtocolDecoderTest.java @@ -0,0 +1,18 @@ +package org.traccar.protocol; + +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; + +public class G1rusProtocolDecoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + var decoder = inject(new G1rusProtocolDecoder(null)); + + verifyPositions(decoder, binary( + "f84604000d85f5e55298004b012ad7d524003d1d0f50726f6d61205361742031303030201556312e31302656312e312e340a03120fa0142b8475a157050401846dfc060175120a29a10d02440111002100310041005107d10ef8")); + + } + +} diff --git a/src/test/java/org/traccar/protocol/GalileoFrameDecoderTest.java b/src/test/java/org/traccar/protocol/GalileoFrameDecoderTest.java index 0d3199eab..530f0930e 100644 --- a/src/test/java/org/traccar/protocol/GalileoFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GalileoFrameDecoderTest.java @@ -1,16 +1,20 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class GalileoFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new GalileoFrameDecoder(); + var decoder = inject(new GalileoFrameDecoder()); + + assertEquals( + binary("01003f01001c475b166133303035333430363431383437393000001d000064897bb003000b0221c20512a60a0000000802000f209d7b8964300f2536fbfd103c1d01"), + decoder.decode(null, null, binary("01003f01001c475b166133303035333430363431383437393000001d000064897bb003000b0221c20512a60a0000000802000f209d7b8964300f2536fbfd103c1d01"))); assertEquals( binary("011780011102e603383633353931303238393630323437043200801c"), diff --git a/src/test/java/org/traccar/protocol/GalileoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GalileoProtocolDecoderTest.java index 66b375b8a..2611832ff 100644 --- a/src/test/java/org/traccar/protocol/GalileoProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GalileoProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class GalileoProtocolDecoderTest extends ProtocolTest { @@ -8,12 +8,15 @@ public class GalileoProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new GalileoProtocolDecoder(null); + var decoder = inject(new GalileoProtocolDecoder(null)); verifyPositions(decoder, binary( "011801018202130338363833343530333230343234323604640010a406207caa9f5b300c830a7901ca0ec802330000000034b802350540003e41703f422b1043234504004600e09000000000a000a100a200a300a400a500a600a700a800a900aa00ab00ac00ad00ae00af00b00000b10000b20000b30000b40000b50000b60000b70000b80000b90000c000000000c100000000c200000000c300000000c400c500c600c700c800c900ca00cb00cc00cd00ce00cf00d000d100d200d4d3140000d60000d70000d80000d90000da0000db00000000dc00000000dd00000000de00000000df00000000f000000000f100000000f200000000f300000000f400000000f500000000f600000000f700000000f800000000f9000000008960")); verifyPositions(decoder, binary( + "01bf83043200101ee4209832bc62300549589302511aaa013300002e00342e02350440003b41b15d42d50e4326450e0046040050000051000052fc5c5300006100008b009000000000d400000000e201000000e376000000e4efce0100e53b590200e600000000e773000000e800000000e9a002d007ea140000d6021b00f8430220ac760000000000000000043200101de4201232bc62300549589302511aaa013300002e00342e02350440012b41b55d42d40e4326450e0046040050000051000052145d5300006100008b009000000000d400000000e201000000e376000000e4efce0100e53b590200e600000000e773000000e800000000e9a002d007ea140000d6021b00f8430220ac760000000000000000043200101ce4208e2ebc62300549589302511aaa013300002e00342e02350440013b41a95d42cd0e4325450f0046040050000051000052235d5300006100008b009000000000d400000000e201000000e376000000e4efce0100e53b590200e600000000e773000000e802000000e9a002d007ea140000d6021b00f8430220ac760000000000000000043200101be4208b2ebc62300549589302511aaa013300002e00342e02350440013b41a45d42cd0e432545090046040050000051000052115d5300006100008b009000000000d400000000e201000000e375000000e48ac90100e53a590200e673000000e773000000e806000000e9a002d007ea140000d6021b00f8430220ac760000000000000000043200101ae420642ebc62300549589302511aaa013300002e00342e02350440013b419f5d42cd0e4324450b00460600500000519313521c5d5300006100008b009000000000d400000000e201000000e300000000e406000000e5c5580200e673000000e700000000e801000000e9a002d007ea140000d6021b00f8430220ac7600000000000000000432001019e420632ebc62300549589302511aaa013300002e00342e02350440013b41725d42cd0e4324450b0046060050000051ab1352035d5300006100008b009000000000d400000000e201000000e300000000e406000000e5c5580200e673000000e700000000e8d6021b00e9a002d007ea140000d6021b00f8430220ac7600000000000000000432001018e4205c2ebc62300549589302511aaa013300002e00342e02350440013b41955d42cd0e4324450a00460400500000510000520b5d5300006100008b009000000000d400000000e201000000e30d000000e4a4350000e5c5580200e600000000e700000000e8d6021b00e9a002d007ea140000d6021b00f8430220ac76000000000000000099f3")); + + verifyPositions(decoder, binary( "017583018202120338363833343530333230363635373304520010384520c850975b300cc03a910107cbf9023365000607341300350640012a41236a4215104329450400460020500000510000520000530000540000550000c000000000c100000000c44bc500c6ffc700c800c900ca00cb00d4993b0500d64100d70000d8be02d90000da0000db00000000dc00000000dd00000000de00000000df00000000f000000000f100000000f200000000f300000000018202120338363833343530333230363635373304520010394520c950975b300cab3a91010ecbf902336000be06341300350640012a41266a4216104329450400460020500000510000520000530000540000550000c000000000c100000000c44bc500c6ffc700c800c900ca00cb00d49b3b0500d64100d70000d8bc02d90000da0000db00000000dc00000000dd00000000de00000000df00000000f000000000f100000000f200000000f3000000000182021203383638333435303332303636353733045200103a4520ca50975b300c953a910113cbf9023358008f06341300350640012a41206a4215104329450400460020500000510000520000530000540000550000c000000000c100000000c44bc500c6ffc700c800c900ca00cb00d49e3b0500d64100d70000d8ba02d90000da0000db00000000dc00000000dd00000000de00000000df00000000f000000000f100000000f200000000f3000000000182021203383638333435303332303636353733045200103b45204251975b300c6d3a91011dcbf9023300008a06341300350640013a41726a4216104329450400460020500000510000520000530000540000550000c000000000c100000000c44bc500c6ffc700c800c900ca00cb00d4a33b0500d64800d70000d80003d90000da0000db00000000dc00000000dd00000000de00000000df00000000f000000000f100000000f200000000f3000000000182021203383638333435303332303636353733045200103c4520bb51975b300c6d3a91011dcbf9023300008a06341300350640013a41816a4216104329450400460020500000510000520000530000540000550000c000000000c100000000c44bc500c6ffc700c800c900ca00cb00d4a33b0500d64800d70000d80003d90000da0000db00000000dc00000000dd00000000de00000000df00000000f000000000f100000000f200000000f300000000e007")); verifyNull(decoder, binary( @@ -42,4 +45,21 @@ public class GalileoProtocolDecoderTest extends ProtocolTest { } + @Test + public void testDecodeIridium() throws Exception { + + var decoder = inject(new GalileoProtocolDecoder(null)); + + decoder.setCompressed(false); + + verifyPosition(decoder, binary( + "01004e01001c0747ea59333030323334303639363034353930000012000063c85e6903000b0321a8f846aba50000000202001e205f5ec863300c4643fdfdbbe6c8fb330000000034e7013505d400000000")); + + /*decoder.setCompressed(true); + + verifyPosition(decoder, binary( + "01003a01001c07553e40333030323334303639363034353930020021000063d1921c03000b0321ac2c4545b20000009f02000ac200000000db00000000"));*/ + + } + } diff --git a/src/test/java/org/traccar/protocol/GalileoProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/GalileoProtocolEncoderTest.java index b25100c48..af640f3d4 100644 --- a/src/test/java/org/traccar/protocol/GalileoProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/GalileoProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; @@ -9,7 +9,7 @@ public class GalileoProtocolEncoderTest extends ProtocolTest { @Test public void testEncode() throws Exception { - var encoder = new GalileoProtocolEncoder(null); + var encoder = inject(new GalileoProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/GatorProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GatorProtocolDecoderTest.java index 24686e220..6f1bd88db 100644 --- a/src/test/java/org/traccar/protocol/GatorProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GatorProtocolDecoderTest.java @@ -1,9 +1,9 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class GatorProtocolDecoderTest extends ProtocolTest { @@ -17,7 +17,7 @@ public class GatorProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new GatorProtocolDecoder(null); + var decoder = inject(new GatorProtocolDecoder(null)); verifyAttributes(decoder, binary( "242480002600341cad190917022021812497260280594200000000c047010000135400009bb600ff00b90d")); diff --git a/src/test/java/org/traccar/protocol/GatorProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/GatorProtocolEncoderTest.java new file mode 100644 index 000000000..af6c71e37 --- /dev/null +++ b/src/test/java/org/traccar/protocol/GatorProtocolEncoderTest.java @@ -0,0 +1,23 @@ +package org.traccar.protocol; + +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; +import org.traccar.model.Command; +import org.traccar.model.Device; + +import static org.mockito.Mockito.when; + +public class GatorProtocolEncoderTest extends ProtocolTest { + + @Test + public void testEncode() throws Exception { + var encoder = inject(new GatorProtocolEncoder(null)); + var device = encoder.getCacheManager().getObject(Device.class, 1); + when(device.getUniqueId()).thenReturn("13332082112"); + + Command command = new Command(); + command.setDeviceId(1); + command.setType(Command.TYPE_POSITION_SINGLE); + verifyCommand(encoder, command, binary("24243000062008958C070D")); + } +} diff --git a/src/test/java/org/traccar/protocol/GenxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GenxProtocolDecoderTest.java index 382c974ce..0f90c9b07 100644 --- a/src/test/java/org/traccar/protocol/GenxProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GenxProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class GenxProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class GenxProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new GenxProtocolDecoder(null); + var decoder = inject(new GenxProtocolDecoder(null)); decoder.setReportColumns("28,2,3,4,13,17,10,23,27,11,7,8,46,56,59,70,74,75,77,89,90,93,99,107,112,113,114,176,175,178,181,182"); diff --git a/src/test/java/org/traccar/protocol/Gl100ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gl100ProtocolDecoderTest.java index 50f993839..d835f2f27 100644 --- a/src/test/java/org/traccar/protocol/Gl100ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gl100ProtocolDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Gl100ProtocolDecoderTest extends ProtocolTest { @@ -9,7 +9,7 @@ public class Gl100ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Gl100ProtocolDecoder(null); + var decoder = inject(new Gl100ProtocolDecoder(null)); verifyPosition(decoder, text( "+RESP:GTLGL,359464030492644,1,2,1,0,0.4,0,299.7,1,5.455551,51.449776,20160311083229,0204,0016,03EC,BD94,00,0036,0102090501")); diff --git a/src/test/java/org/traccar/protocol/Gl200BinaryProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gl200BinaryProtocolDecoderTest.java index b8ce8af5d..680b3b77c 100644 --- a/src/test/java/org/traccar/protocol/Gl200BinaryProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gl200BinaryProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Gl200BinaryProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class Gl200BinaryProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Gl200BinaryProtocolDecoder(null); + var decoder = inject(new Gl200BinaryProtocolDecoder(null)); verifyPosition(decoder, binary( "2b4556542d00fc1fbf0063450102020956325403000343056437f8220700000200000000010000160100f2007eff75a1f0025c6b1a07e1080108241a02680003189c1ac500000000000002100800000000000000000007e1080108241a19e24e4e0d0a")); diff --git a/src/test/java/org/traccar/protocol/Gl200FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Gl200FrameDecoderTest.java index 17eed8a59..6fd3f0aaa 100644 --- a/src/test/java/org/traccar/protocol/Gl200FrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gl200FrameDecoderTest.java @@ -1,16 +1,16 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class Gl200FrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Gl200FrameDecoder(); + var decoder = inject(new Gl200FrameDecoder()); assertEquals( binary("2b4c474e00ff0026fe110b07020106563454040d054905000007e4031911213905083abd0d0a"), diff --git a/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java index 429ee15c7..3f8a4d0c2 100644 --- a/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,47 @@ public class Gl200TextProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Gl200TextProtocolDecoder(null); + var decoder = inject(new Gl200TextProtocolDecoder(null)); + + verifyNull(decoder, buffer( + "+RESP:GTFRI,8020040200,866314060109269,,,10,1,1,0.0,0,9.0,-71.596601,-33.524595,20230722145338,0730,0001,772A,052B253E,00,0.0,,,,,100,210100,,,,20230722145341,0F4C$")); + + verifyAttributes(decoder, buffer( + "+RESP:GTCAN,F1040C,862599050497393,GV350M,0,0,FFFFFFFF,,1,,,,,,,,,,,,,,,,,,,,,,,0,,,1,0.0,70,2961.6,-78.691750,-0.951135,20230703191659,,,,,,20230703191659,5A4A$")); + + verifyAttribute(decoder, buffer( + "+RESP:GTBAA,F1040C,862599050497393,GV350M,FF,3,0,04,000A,780541256AE9,3065,0,0.0,213,2908.3,-78.691944,-0.951426,20230511173150,,,,,,20230511175001,0159$"), + "accessoryVoltage", 3.065); + + verifyAttribute(decoder, buffer( + "+RESP:GTBID,C20105,866833040163013,GV350M,1,0,000A,B80EA11FF800,2934,0,0.0,0,1506.5,-99.192686,18.932709,20221026025339,0334,0020,0232,029D4E02,,20221026181026,9F1D$"), + "accessoryVoltage", 2.934); + + verifyAttribute(decoder, buffer( + "+RESP:GTDTT,410502,864802030541621,,,,1,35,45637561747261636b0d0a434f4d422c302c39342e302c2d312e302c2c2c4844430d0a,20230421034626,EA2E$"), + Position.KEY_FUEL_LEVEL, 94.0); + + verifyNull(decoder, buffer( + "+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,,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,,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( + "+RESP:GTDAR,F10406,865284049582228,,4,0,,,1,18.5,0,129.4,114.015430,22.537279,20210922004634,0460,0000,27BD,0DFC,,,,20210922004635,082B$"), + "warningType", 4); + + verifyAttribute(decoder, buffer( + "+RESP:GTNMR,423033,355197370058831,,0,0,0,1,0,0.0,298,182.7,-79.257983,43.875152,20220627132020,,,,,15,0,74,20220627144928,03CF$"), + Position.KEY_BATTERY_LEVEL, 74); verifyPosition(decoder, buffer( "+RESP:GTFRI,5E0100,861971050039361,,,,10,1,1,10.4,140,196.9,-80.709946,35.016525,20220302220944,0310,0260,1CE9,52A1,00,0.0,,,,,420000,,,,20220302220948,1B0B$")); diff --git a/src/test/java/org/traccar/protocol/GlobalSatProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GlobalSatProtocolDecoderTest.java index 3f5296f4b..7391fea28 100644 --- a/src/test/java/org/traccar/protocol/GlobalSatProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GlobalSatProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class GlobalSatProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class GlobalSatProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new GlobalSatProtocolDecoder(null); + var decoder = inject(new GlobalSatProtocolDecoder(null)); verifyNull(decoder, text( "GSh,131826789036289,3,M,ea04*3d")); diff --git a/src/test/java/org/traccar/protocol/GlobalSatProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/GlobalSatProtocolEncoderTest.java index ae4b52167..dc00cad44 100644 --- a/src/test/java/org/traccar/protocol/GlobalSatProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/GlobalSatProtocolEncoderTest.java @@ -1,17 +1,17 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class GlobalSatProtocolEncoderTest extends ProtocolTest { @Test - public void testEncodeAlarmDismiss() { + public void testEncodeAlarmDismiss() throws Exception { - var encoder = new GlobalSatProtocolEncoder(null); + var encoder = inject(new GlobalSatProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); @@ -22,9 +22,9 @@ public class GlobalSatProtocolEncoderTest extends ProtocolTest { } @Test - public void testEncodeOutputControl() { + public void testEncodeOutputControl() throws Exception { - var encoder = new GlobalSatProtocolEncoder(null); + var encoder = inject(new GlobalSatProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/GlobalstarProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GlobalstarProtocolDecoderTest.java index 1dd5c2542..8d4210e9d 100644 --- a/src/test/java/org/traccar/protocol/GlobalstarProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GlobalstarProtocolDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; import io.netty.handler.codec.http.HttpMethod; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class GlobalstarProtocolDecoderTest extends ProtocolTest { @@ -9,7 +9,7 @@ public class GlobalstarProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new GlobalstarProtocolDecoder(null); + var decoder = inject(new GlobalstarProtocolDecoder(null)); verifyNull(decoder, request(HttpMethod.POST, "/", buffer( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", @@ -29,7 +29,7 @@ public class GlobalstarProtocolDecoderTest extends ProtocolTest { "<esn>0-2682225</esn>", "<unixTime>1585105370</unixTime>", "<gps>N</gps>", - "<payload length=\"9\" source=\"pc\" encoding=\"hex\">0x8EFE2D97DDEA420018</payload>", + "<payload length=\"9\" source=\"pc\" encoding=\"hex\">0x00C583EACD37210A00</payload>", "</stuMessage>", "</stuMessages>"))); diff --git a/src/test/java/org/traccar/protocol/GnxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GnxProtocolDecoderTest.java index 8bd5e2bd5..78722fa17 100644 --- a/src/test/java/org/traccar/protocol/GnxProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GnxProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class GnxProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class GnxProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new GnxProtocolDecoder(null); + var decoder = inject(new GnxProtocolDecoder(null)); verifyPosition(decoder, text( "$GNX_MIF,865733022354161,143,0,172642,180316,172642,180316,1,13.034581,N,080.234521,E,0,05396274,ROUTE_2#########,Deo ############,GNX04008,B0*")); diff --git a/src/test/java/org/traccar/protocol/GoSafeProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GoSafeProtocolDecoderTest.java index bb79f4f25..3e1349a80 100644 --- a/src/test/java/org/traccar/protocol/GoSafeProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GoSafeProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,11 +9,14 @@ public class GoSafeProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new GoSafeProtocolDecoder(null); + var decoder = inject(new GoSafeProtocolDecoder(null)); verifyPositions(decoder, false, text( "*GS06,357330050846344,RST#")); + verifyPositions(decoder, text( + "*GS06,353218073585128,181255300523,,SYS:Smart Track;V9.31;V1.1.5,GPS:A;5;N31.551856;E74.366920;0;0;;2.15;2.64,COT:0,ADC:10.78;0.02,DTT:4002;E1;0;0;0;1$181325300523,,SYS:Smart Track;V9.31;V1.1.5,GPS:A;6;N31.551856;E74.366920;0;0;;2.05;2.13,COT:0,ADC:10.79;0.02,DTT:4002;E1;0;0;0;1#")); + verifyAttribute(decoder, text( "*GS06,356449068350122,013519070819,,SYS:G6S;V3.37;V1.1.8,GPS:A;12;N23.169866;E113.450728;0;255;54;0.79,COT:18779;,ADC:12.66;0.58,DTT:4084;E1;0;0;0;1,IWD:0;1;ad031652643fff28;23.2;1;1;86031652504fff28;24.3;2;1;e603165252a5ff28;24.2;3;1;bb0416557da6ff28;24.0#"), Position.PREFIX_TEMP + 3, 24.0); diff --git a/src/test/java/org/traccar/protocol/GotopProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GotopProtocolDecoderTest.java index bae187959..aea6e7592 100644 --- a/src/test/java/org/traccar/protocol/GotopProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GotopProtocolDecoderTest.java @@ -1,18 +1,30 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; +import org.traccar.model.Position; public class GotopProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new GotopProtocolDecoder(null); + var decoder = inject(new GotopProtocolDecoder(null)); verifyNull(decoder, text( "")); - + + verifyAttribute(decoder, text( + "867688033841044,ALM-B1-1,A,DATE:190622,TIME:144956,LAT:22.7193976N,LON:114.3878200E,Speed:000.1,100-22,03.72"), + Position.KEY_ALARM, Position.ALARM_GEOFENCE_ENTER); + + verifyAttribute(decoder, text( + "860264050778076,CMD-KEY,V,DATE:220516,TIME:090224,LAT:48.7592616N,LON:003.4658121W,Speed:000.0,057-21,01.60"), + Position.KEY_ALARM, Position.ALARM_SOS); + + verifyPosition(decoder, text( + "860264050778076,CMD-T,A,DATE:220516,TIME:100627,LAT:48.7636978N,LON:003.4652398W,Speed:051.4,077-24,01.00,WIFI:{84-a0-6e-94-42-5e&-47,b2-22-7a-56-5a-2a&-48,b4-b0-24-09-91-d4&-76,18-3c-b7-1f-da-67&-83,08-86-3b-94-78-44&-85,40-24-b2-c5-0f-5e&-87,b0-e5-ed-4e-06-61&-87,60-1d-9d-a6-d2-5d&-88,14-eb-b6-a5-55-8c&-88,40-18-b1-d7-28-54&-88},BLE:{1f-cf-4d-3c-61-bf&-91,39-82-d0-ba-34-69&-57,df-3a-31-ac-ad-72&-73,7c-d9-f4-11-0b-a0&-78,03-b5-9f-45-bd-0d&-88}")); + verifyNull(decoder, text( "353327020412763,CMD-X")); diff --git a/src/test/java/org/traccar/protocol/Gps056FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Gps056FrameDecoderTest.java index f6524f82b..332423645 100644 --- a/src/test/java/org/traccar/protocol/Gps056FrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gps056FrameDecoderTest.java @@ -1,16 +1,16 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class Gps056FrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Gps056FrameDecoder(); + var decoder = inject(new Gps056FrameDecoder()); assertEquals( binary("242435314750534c5f30323836323436323033333738323934361905110f160b0b7710584e1cbd1b9b4500005b100300fb0a071700ffff23"), diff --git a/src/test/java/org/traccar/protocol/Gps056ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gps056ProtocolDecoderTest.java index 01fa98ba4..29cc6b09f 100644 --- a/src/test/java/org/traccar/protocol/Gps056ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gps056ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Gps056ProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class Gps056ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Gps056ProtocolDecoder(null); + var decoder = inject(new Gps056ProtocolDecoder(null)); verifyNull(decoder, buffer( "$$25LOGN_118624620337829462.1#")); diff --git a/src/test/java/org/traccar/protocol/Gps103ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gps103ProtocolDecoderTest.java index 79dc8ab7b..c11a5b0d9 100644 --- a/src/test/java/org/traccar/protocol/Gps103ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gps103ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,11 @@ public class Gps103ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Gps103ProtocolDecoder(null); + var decoder = inject(new Gps103ProtocolDecoder(null)); + + verifyAttribute(decoder, text( + "imei:865456055519122,sensor alarm,2208011920,,L,;"), + Position.KEY_ALARM, Position.ALARM_VIBRATION); verifyPosition(decoder, text( "imei:864035050002451,tracker,201223064947,,F,064947,A,1935.70640,N,09859.94436,W,0.025,;")); @@ -155,7 +159,7 @@ public class Gps103ProtocolDecoderTest extends ProtocolTest { "359586015829802")); // No GPS signal - verifyNull(decoder, text( + verifyNotNull(decoder, text( "imei:359586015829802,tracker,000000000,13554900601,L,;")); verifyPosition(decoder, text( diff --git a/src/test/java/org/traccar/protocol/Gps103ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Gps103ProtocolEncoderTest.java index 5edfbf629..3df64039c 100644 --- a/src/test/java/org/traccar/protocol/Gps103ProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/Gps103ProtocolEncoderTest.java @@ -1,17 +1,17 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class Gps103ProtocolEncoderTest extends ProtocolTest { @Test public void testEncodePositionPeriodic() throws Exception { - var encoder = new Gps103ProtocolEncoder(null); + var encoder = inject(new Gps103ProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); @@ -25,7 +25,7 @@ public class Gps103ProtocolEncoderTest extends ProtocolTest { @Test public void testEncodeCustom() throws Exception { - var encoder = new Gps103ProtocolEncoder(null); + var encoder = inject(new Gps103ProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/GpsGateProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GpsGateProtocolDecoderTest.java index c935fb3c2..000163ba8 100644 --- a/src/test/java/org/traccar/protocol/GpsGateProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GpsGateProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class GpsGateProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class GpsGateProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new GpsGateProtocolDecoder(null); + var decoder = inject(new GpsGateProtocolDecoder(null)); verifyPosition(decoder, text( "$FRCMD,0097,_SendMessage,,7618.51990,S,4002.26182,E,350.0,1.08,0.0,250816,183522.000,0*7F")); diff --git a/src/test/java/org/traccar/protocol/GpsMarkerProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GpsMarkerProtocolDecoderTest.java index c0b4966a8..8a5cb434b 100644 --- a/src/test/java/org/traccar/protocol/GpsMarkerProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GpsMarkerProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; @@ -9,7 +9,7 @@ public class GpsMarkerProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new GpsMarkerProtocolDecoder(null); + var decoder = inject(new GpsMarkerProtocolDecoder(null)); verifyPosition(decoder, text( "$GM23D863071014445404T260816142611N55441051E037325071033063C0530304#")); diff --git a/src/test/java/org/traccar/protocol/GpsmtaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GpsmtaProtocolDecoderTest.java index 7170718f6..73da69d96 100644 --- a/src/test/java/org/traccar/protocol/GpsmtaProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GpsmtaProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class GpsmtaProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class GpsmtaProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new GpsmtaProtocolDecoder(null); + var decoder = inject(new GpsmtaProtocolDecoder(null)); verifyPosition(decoder, text( "3085a94ef459 1446536867 49.81621 24.054207 1 0 22 0 10 12 24 0 0")); diff --git a/src/test/java/org/traccar/protocol/GranitFrameDecoderTest.java b/src/test/java/org/traccar/protocol/GranitFrameDecoderTest.java index b4e24f961..9d6c189f6 100644 --- a/src/test/java/org/traccar/protocol/GranitFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GranitFrameDecoderTest.java @@ -1,16 +1,16 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class GranitFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new GranitFrameDecoder(); + var decoder = inject(new GranitFrameDecoder()); assertEquals( binary("2b525243427e1a003e2934757c57b8b03c38d279b4e61e9bd7006b000000001c00002a4533"), diff --git a/src/test/java/org/traccar/protocol/GranitProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GranitProtocolDecoderTest.java index 7fd5ffe0e..ff45bd99c 100644 --- a/src/test/java/org/traccar/protocol/GranitProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GranitProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class GranitProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class GranitProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new GranitProtocolDecoder(null); + var decoder = inject(new GranitProtocolDecoder(null)); verifyPositions(decoder, binary( "2b444441547e8400c500040130050c43495808002839aee3150200000000640000000000000008002839aee3150200000000640000000000000008002839aee3150200000000640000000000000008002839aee3150200000000640000000000000008002839aee3150200000000640000000000000008002839aee3150200000000640000000000000014002a37420d0a")); diff --git a/src/test/java/org/traccar/protocol/Gs100ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gs100ProtocolDecoderTest.java index 68c9b8219..c382b3262 100644 --- a/src/test/java/org/traccar/protocol/Gs100ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gs100ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Gs100ProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class Gs100ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Gs100ProtocolDecoder(null); + var decoder = inject(new Gs100ProtocolDecoder(null)); verifyNull(decoder, binary( "474C490F383632343632303332353036373030133839333831303131363039313838343837323546084657312E302E3236")); diff --git a/src/test/java/org/traccar/protocol/Gt02ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gt02ProtocolDecoderTest.java index 9d36ef346..100ef340a 100644 --- a/src/test/java/org/traccar/protocol/Gt02ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gt02ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Gt02ProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class Gt02ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Gt02ProtocolDecoder(null); + var decoder = inject(new Gt02ProtocolDecoder(null)); verifyAttributes(decoder, binary( "6868150000035889905895258400831c07415045584f4b210d0a")); diff --git a/src/test/java/org/traccar/protocol/Gt06FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Gt06FrameDecoderTest.java index 059674398..c74427f90 100644 --- a/src/test/java/org/traccar/protocol/Gt06FrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gt06FrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Gt06FrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class Gt06FrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Gt06FrameDecoder(); + var decoder = inject(new Gt06FrameDecoder()); verifyFrame( binary("787803691604130318491475905BD30E25001E10BBF7635D14759006E626560501CC0028660F213228660F1F2828660EA81E286610731428660F20140D0A"), diff --git a/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java index b65b6709e..9b196e501 100644 --- a/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,7 @@ public class Gt06ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Gt06ProtocolDecoder(null); + var decoder = inject(new Gt06ProtocolDecoder(null)); verifyNull(decoder, binary( "787805120099abec0d0a")); @@ -18,6 +18,78 @@ public class Gt06ProtocolDecoderTest extends ProtocolTest { "78780D01086471700328358100093F040D0A")); verifyAttribute(decoder, binary( + "78785995ffff01170719152013df0163d45f041ee52018be002f00876900004556454e545f3836323739383035303137353131325f30303030303030305f323032335f30375f32355f31385f33325f30355f31342e6d70340119d15a0d0a"), + Position.KEY_EVENT, 0x69); + + verifyAttribute(decoder, binary( + "787829a01707150f2d0ecd01635100041e96d000087c02d4020000912e000000000718798d000e0006ed3ce50d0a"), + Position.KEY_IGNITION, false); + + verifyNotNull(decoder, binary( + "787829a0170704112226cf0163fe7c0420f6f000091302d402000091290000000007186b8f01030001460d010d0a")); + + verifyAttribute(decoder, binary( + "797900109b0344373532304136320d0a000f87f00d0a"), + Position.KEY_RESULT, "D7520A62"); + + verifyAttribute(decoder, binary( + "7878241617070a150e24ca01fba0040780e177005c0001720253360027db6204e40400004bf1e90d0a"), + Position.KEY_ALARM, Position.ALARM_SOS); + + verifyAttribute(decoder, binary( + "78780a130604ea04000006bc8a0d0a"), + Position.KEY_POWER, null); + + verifyAttributes(decoder, binary( + "797900849404414c4d313d43353b414c4d323d43433b414c4d333d35433b535441313d43303b4459443d30313b534f533d303133323838333730302c2c3b43454e5445523d303133323838333730303b46454e43453d46656e63652c4f46462c302c302e3030303030302c302e3030303030302c3330302c494e206f72204f55542c313b00b79d120d0a")); + + verifyAttribute(decoder, binary( + "78782912170316053b3bcf015b51220af1201105d56100000000000000000000869c0130010000000238d1af0d0a"), + Position.KEY_DRIVING_TIME, 0); + + verifyAttribute(decoder, binary( + "78781219012ed042cc00954d00040419000056fe290d0a"), + Position.KEY_ALARM, Position.ALARM_LOW_BATTERY); + + verifyAttribute(decoder, binary( + "78782DA4150817073B10CF032EEA9C0B6CE0800015141001CC0100009A00000000000A6F24014605041900FF01908A640D0A"), + Position.KEY_ALARM, Position.ALARM_LOW_BATTERY); + + verifyAttribute(decoder, binary( + "78782627100419092D07C5027AC91C0C4658000005370900000000000000008002001900FF00004DF60D0A"), + Position.KEY_ALARM, Position.ALARM_LOW_BATTERY); + + verifyPosition(decoder, binary( + "78781511170103100e1f9904efe30400a97f88003410ffdd000d0a")); + + verifyAttribute(decoder, binary( + "787819a5012ed0000075df000000000098661502050413000019a12b0d0a"), + Position.KEY_ALARM, Position.ALARM_TAMPERING); + + verifyAttribute(decoder, binary( + "7878131302801900002e42016f000000003a0177ef180d0a"), + Position.KEY_POWER, 3.67); + + verifyAttribute(decoder, binary( + "78782526160913063918c002780fab0c44750f00040008027f14084c0038420600030c020007398e0d0a"), + Position.KEY_ALARM, Position.ALARM_TAMPERING); + + verifyAttribute(decoder, binary( + "7979000d940516090908081c030defbd2d0d0a"), + Position.KEY_DOOR, true); + + verifyAttribute(decoder, binary( + "78782516160908063736c0006e70110442fc800000000800000000000000000300002512000473fb0d0a"), + Position.KEY_ALARM, Position.ALARM_TAMPERING); + + verifyPosition(decoder, binary( + "78782e2416061a103600c80275298404a0a24000184602d4023a49006f060104ed01940000086508004139765000be7d640d0a")); + + verifyAttribute(decoder, binary( + "79790019941b524649443a3030384642324245424133390d0a000c14930d0a"), + "serial", "RFID:008FB2BEBA39"); + + verifyAttribute(decoder, binary( "7878241216040e102c22cf00915ffb04c6016300195a02d402283b00753f400571040001dda4880d0a"), Position.KEY_IGNITION, false); @@ -54,8 +126,9 @@ public class Gt06ProtocolDecoderTest extends ProtocolTest { "7878281520000000003c49434349443a38393838323339303030303039373330323635303e00020d446f260d0a"), Position.KEY_ICCID, "89882390000097302650"); - verifyNull(decoder, binary( - "797900099b0380d600046f91e90d0a")); + verifyAttribute(decoder, binary( + "797900099b0380d600046f91e90d0a"), + Position.KEY_RESULT, "80d600"); verifyNull(decoder, binary( "797900a56615010d081f3b012c323131303d30303033643238342c323130353d30303030316332302c323130623d30303030326537632c323130633d30303033643238342c323130663d30303030306331632c323130643d30303030323166632c323161363d30303030303030302c323130343d30303030306531302c323132663d30303030303030302c323134353d30303030303030302ccb03851f5f03c020525514a7003e216a0d0a")); @@ -68,7 +141,7 @@ public class Gt06ProtocolDecoderTest extends ProtocolTest { verifyAttribute(decoder, binary( "7979000E9B0332382E33A1E60D0A0289BE490D0A"), - Position.PREFIX_TEMP + 1, 28.3); + Position.KEY_RESULT, "32382e33a1e60d0a"); verifyPosition(decoder, binary( "7878353714080d05000ac500a886eb0b7522f000100001fe0a05ea004f1b000001002e0400002328003b0217c0003c0401020001002c468a0d0a")); @@ -111,7 +184,7 @@ public class Gt06ProtocolDecoderTest extends ProtocolTest { verifyAttribute(decoder, binary( "797900149b03023539303042343843454238410300139ba40d0a"), - Position.KEY_DRIVER_UNIQUE_ID, "5900B48CEB"); + Position.KEY_RESULT, "0235393030423438434542384103"); verifyPosition(decoder, binary( "787821121303120b2524c70138e363085b549003d43301940057d200cd52c000006aa1ca0d0a")); @@ -144,7 +217,7 @@ public class Gt06ProtocolDecoderTest extends ProtocolTest { verifyAttributes(decoder, binary( "79790008940000ed0289d6860d0a")); - verifyNull(decoder, binary( + verifyAttributes(decoder, binary( "797900a59404414c4d313d34353b414c4d323d44353b414c4d333d35353b535441313d34303b4459443d30313b534f533d303538353036313536372c2c3b43454e5445523d3b46454e43453d46656e63652c4f46462c302c302e3030303030302c302e3030303030302c3330302c494e206f72204f55542c303b49434349443d38393937313033313031303038393539303432463b4d4f44453d4d4f44452c312c3138303b0008f65e0d0a")); verifyPosition(decoder, binary( diff --git a/src/test/java/org/traccar/protocol/Gt06ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Gt06ProtocolEncoderTest.java index 79165d9dc..80b50df3a 100644 --- a/src/test/java/org/traccar/protocol/Gt06ProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/Gt06ProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; @@ -9,7 +9,7 @@ public class Gt06ProtocolEncoderTest extends ProtocolTest { @Test public void testEncode() throws Exception { - var encoder = new Gt06ProtocolEncoder(null); + var encoder = inject(new Gt06ProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/Gt30ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gt30ProtocolDecoderTest.java index 24addc8b4..4d7e8699e 100644 --- a/src/test/java/org/traccar/protocol/Gt30ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gt30ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Gt30ProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class Gt30ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Gt30ProtocolDecoder(null); + var decoder = inject(new Gt30ProtocolDecoder(null)); verifyPosition(decoder, text( "$$005D3037811014 9955102834.000,A,3802.8629,N,02349.7163,E,0.00,,060117,,*13|1.3|26225BD")); diff --git a/src/test/java/org/traccar/protocol/H02FrameDecoderTest.java b/src/test/java/org/traccar/protocol/H02FrameDecoderTest.java index 2294b773b..dc6eec84d 100644 --- a/src/test/java/org/traccar/protocol/H02FrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/H02FrameDecoderTest.java @@ -1,16 +1,16 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class H02FrameDecoderTest extends ProtocolTest { @Test public void testDecodeShort() throws Exception { - var decoder = new H02FrameDecoder(0); + var decoder = inject(new H02FrameDecoder(0)); assertEquals( binary("2a48512c3335353438383032303131333931312c56312c3031323934352c412c353233312e37393238332c4e2c30313332342e31303731382c452c302e30352c302c3137303231372c464646464646464623"), @@ -37,7 +37,7 @@ public class H02FrameDecoderTest extends ProtocolTest { @Test public void testDecodeLong() throws Exception { - var decoder = new H02FrameDecoder(0); + var decoder = inject(new H02FrameDecoder(0)); assertEquals( binary("24410600082621532131081504419390060740418306000000fffffbfdff0015060000002c02dc0c000000001f"), @@ -48,7 +48,7 @@ public class H02FrameDecoderTest extends ProtocolTest { @Test public void testDecodeAlternative() throws Exception { - var decoder = new H02FrameDecoder(0); + var decoder = inject(new H02FrameDecoder(0)); assertEquals( binary("2a48512c343230363131393133302c4e42522c3130323430332c3233382c312c302c372c313131312c323236342c36332c313131312c323236352c35382c313131312c323236362c35302c313131312c333133352c33372c313131312c3630352c33332c313131312c343932302c33302c313131312c3630372c32382c3131303131372c46464646444646462c3623"), diff --git a/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java index ad5f82176..a63488960 100644 --- a/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,10 @@ public class H02ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new H02ProtocolDecoder(null); + var decoder = inject(new H02ProtocolDecoder(null)); + + verifyPosition(decoder, buffer( + "*HQ,3177718238,V6,002926,V,3514.4088,N,9733.2842,W,0.00,0.00,151222,FFF7FBFF,310,260,32936,13641,8944501311217563382F,#")); verifyPosition(decoder, buffer( "*HQ,5905101893,V1,105759,A,37573392,S,145037022,E,000.00,173,280122,FF7FFBFF,,,9059e2c,8232,4#")); @@ -270,7 +273,7 @@ public class H02ProtocolDecoderTest extends ProtocolTest { @Test public void testDecodeStatus() throws Exception { - var decoder = new H02ProtocolDecoder(null); + var decoder = inject(new H02ProtocolDecoder(null)); verifyAttribute(decoder, buffer( "*HQ,2705171109,V1,213324,A,5002.5849,N,01433.7822,E,0.00,000,140613,FFFFFFFF#"), diff --git a/src/test/java/org/traccar/protocol/H02ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/H02ProtocolEncoderTest.java index 3ed0a7e91..2fc7af958 100644 --- a/src/test/java/org/traccar/protocol/H02ProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/H02ProtocolEncoderTest.java @@ -1,6 +1,7 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; @@ -10,14 +11,19 @@ import java.time.LocalTime; import java.time.ZoneOffset; import java.util.Date; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class H02ProtocolEncoderTest extends ProtocolTest { - private H02ProtocolEncoder encoder = new H02ProtocolEncoder(null); - private Date time = Date.from( + private H02ProtocolEncoder encoder; + private final Date time = Date.from( LocalDateTime.of(LocalDate.now(), LocalTime.of(1, 2, 3)).atZone(ZoneOffset.systemDefault()).toInstant()); + @BeforeEach + public void before() throws Exception { + encoder = inject(new H02ProtocolEncoder(null)); + } + @Test public void testAlarmArmEncode() { diff --git a/src/test/java/org/traccar/protocol/HaicomProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HaicomProtocolDecoderTest.java index 9d80940ea..5ad5693be 100644 --- a/src/test/java/org/traccar/protocol/HaicomProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/HaicomProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class HaicomProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class HaicomProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new HaicomProtocolDecoder(null); + var decoder = inject(new HaicomProtocolDecoder(null)); verifyPosition(decoder, text( "$GPRS012497007097169,T100001,150618,230031,5402267400332464,0004,2014,000001,,,1,00#V040*"), diff --git a/src/test/java/org/traccar/protocol/HomtecsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HomtecsProtocolDecoderTest.java index 192aa3010..ac814a5f4 100644 --- a/src/test/java/org/traccar/protocol/HomtecsProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/HomtecsProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class HomtecsProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class HomtecsProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new HomtecsProtocolDecoder(null); + var decoder = inject(new HomtecsProtocolDecoder(null)); verifyNull(decoder, text( "MDS0001_R6d1821f7,170323,143601.00,04,,,,,,,,,")); diff --git a/src/test/java/org/traccar/protocol/HoopoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HoopoProtocolDecoderTest.java index 3ee0a5e01..8d57bf787 100644 --- a/src/test/java/org/traccar/protocol/HoopoProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/HoopoProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class HoopoProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class HoopoProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new HoopoProtocolDecoder(null); + var decoder = inject(new HoopoProtocolDecoder(null)); verifyPosition(decoder, text( "{ \"deviceId\": \"BCCD0654\", \"assetName\": \"BCCD0654\", \"assetType\": \"???? ?????? - ??? 8\", \"eventData\": { \"latitude\": 31.97498, \"longitude\": 34.80802, \"locationName\": \"\", \"accuracyLevel\": \"High\", \"eventType\": \"Arrival\", \"batteryLevel\": 100, \"receiveTime\": \"2021-09-20T18:52:32Z\" }, \"eventTime\": \"2021-09-20T08:52:02Z\", \"serverReportTime\": \"0001-01-01T00:00:00Z\" }")); diff --git a/src/test/java/org/traccar/protocol/HuaShengFrameDecoderTest.java b/src/test/java/org/traccar/protocol/HuaShengFrameDecoderTest.java index 228f41ee8..26494f2b9 100644 --- a/src/test/java/org/traccar/protocol/HuaShengFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/HuaShengFrameDecoderTest.java @@ -1,16 +1,16 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class HuaShengFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new HuaShengFrameDecoder(); + var decoder = inject(new HuaShengFrameDecoder()); assertEquals( binary("c0010c00120060000000000004000600010100c0"), diff --git a/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java index 74671b845..d8cc739ab 100644 --- a/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,7 @@ public class HuaShengProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new HuaShengProtocolDecoder(null); + var decoder = inject(new HuaShengProtocolDecoder(null)); verifyNull(decoder, binary( "c00000007eaa000000000000cb8000000032313130313030393238323800e9abafffd615d2000000000008000000010015ffffff0000000000000004e7ffffffffff0005000a10080001d5ab000900154b4e4142323531324d4b54353638363630000f00133335343434343131353130333138380014000b00000000000000c0")); @@ -21,6 +21,13 @@ public class HuaShengProtocolDecoderTest extends ProtocolTest { "c000000077aa0200000000000e000100143347315f48312e315f56312e30372e54000300133335353835353035303434303635380004000b3531323030303000050005010006000400070004000800050000090018383936313032353431343533333239313833360d000a000f796573696e7465726e6574c0")); verifyAttribute(decoder, binary( + "c0000000bdaa0000000000061d480000083233303132333039323634330000000000000000000000a600140000000100187e02de0a00290372000005951600260000004a0000040009080000004a0005000a0d0000000ad0000900154d414b474d363639484a4e333031383739000f00133836323230353035353338393836320010001031333231322e30303030303000110008000000000014000bf851084f000018001500060000002000153430344030354035363532403130363332c0"), + Position.KEY_ODOMETER, 13212000.0); + + verifyNotNull(decoder, binary( + "c0000000b9aa00000000000013c800001132333035303431343537323600186bc30045e5b8002a008b0077002d000100187f0c4b2600d906ec000005938800000000000e0000040009110000000e0005000a1d0400000079000900154646464646464646464646464646464646000f00133836323230353035353339313733360010000c302e30303030303000110008000000000014000bf81b204901b52a001500060000002000153231394030324030403130343438393139c0")); + + verifyAttribute(decoder, binary( "C00000001CAA120000000000020001001001000200030043008200C100C0"), Position.KEY_DTCS, "P0100 P0200 P0300 C0300 B0200 U0100"); @@ -29,6 +36,9 @@ public class HuaShengProtocolDecoderTest extends ProtocolTest { Position.KEY_HOURS, 58.7); verifyNotNull(decoder, binary( + "c0000000b1aa000000000000050000000031393730303130313134303200000000000000000000000000000000000100180000000000000000000000000000000000c57e000005000a1a000000c569000900155756575a5a5a43445a4e57313139313534000f00133836393733313035313339393231300010000c302e30303030303000110008000000000014000bf800002800000000150006000000200016353035403031403040313337313931363831c0")); + + verifyNotNull(decoder, binary( "c000000077aa00000000000070020000003230303132373035313635330000000000000000000000000000000000010015ffffffff000000000000019dffffffffff0005000a1f00000e455a00200019313238354031406666666540386233663930634030000f0013333536373236313038313335343530c0")); verifyPosition(decoder, binary( diff --git a/src/test/java/org/traccar/protocol/HuaShengProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/HuaShengProtocolEncoderTest.java new file mode 100644 index 000000000..c320d4aa9 --- /dev/null +++ b/src/test/java/org/traccar/protocol/HuaShengProtocolEncoderTest.java @@ -0,0 +1,33 @@ +package org.traccar.protocol; + +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; +import org.traccar.model.Command; + +public class HuaShengProtocolEncoderTest extends ProtocolTest { + + @Test + public void testEncode() throws Exception { + + var encoder = inject(new HuaShengProtocolEncoder(null)); + + Command command; + + command = new Command(); + command.setDeviceId(1); + command.setType(Command.TYPE_OUTPUT_CONTROL); + command.set(Command.KEY_INDEX, 1); + command.set(Command.KEY_DATA, "1"); + + verifyCommand(encoder, command, binary("c00000000daa1600000000000101c0")); + + command = new Command(); + command.setDeviceId(1); + command.setType(Command.TYPE_POSITION_PERIODIC); + command.set(Command.KEY_FREQUENCY, 60); + + verifyCommand(encoder, command, binary("c000000012aa0400000000000100020006003cc0")); + + } + +} diff --git a/src/test/java/org/traccar/protocol/HuabaoFrameDecoderTest.java b/src/test/java/org/traccar/protocol/HuabaoFrameDecoderTest.java index d4789032d..a76beac20 100644 --- a/src/test/java/org/traccar/protocol/HuabaoFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/HuabaoFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class HuabaoFrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class HuabaoFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new HuabaoFrameDecoder(); + var decoder = inject(new HuabaoFrameDecoder()); verifyFrame( binary("283734303139303331313138352c312c3030312c454c4f434b2c332c35323934333929"), diff --git a/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java index 7aaec33e7..9c3fc164a 100644 --- a/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,15 +9,81 @@ public class HuabaoProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new HuabaoProtocolDecoder(null); + var decoder = inject(new HuabaoProtocolDecoder(null)); + + verifyNull(decoder, binary( + "7e010200204f07788ef67601824f4459344f544d314d4459774d4441314d444977626d5633553235536457786cba7e")); + + verifyAttribute(decoder, binary( + "7e0200003f014501643822000300020000000c000c0000000000000000000000000000230615143903300111310100530901027f0300456f073e56020900fe02001e57080002000200000000df7e"), + Position.KEY_BATTERY_LEVEL, 90); + + verifyAttribute(decoder, binary( + "7e090000344f07788ef87d0138f02305151230460102020001ffffffff000100001457000000020006503134353700000c000a029dc63004b99a98230515132726787e"), + Position.KEY_DTCS, "P1457"); + + verifyAttribute(decoder, binary( + "7e0200006476806111898300710000000000100046005d3156065f7128000000000000230511165956660b01fe000001031a5d1a8101670831333231343332326902018b6a01166b01006c0f323034303830393230373533363735711438393434343738383030303030323131303030464b7e"), + Position.KEY_BATTERY, 3.95); + + verifyAttribute(decoder, binary( + "7e02000071768060874297002d0000000000208022015a30b006c869f8000000000000230505062034600b0004003930303235343939660b01cc0000000c40f89f27b067083133323134333232690201936a01116b01006c0f34363030383138353937303632343071143839383630343938313032313930353835373430607e"), + Position.KEY_EVENT, 4); + + verifyAttribute(decoder, binary( + "7e0200002f017028775424038d000000000000000a0189dbeb04ca653a00000012014723040700074401040000000030011b31010ee1012dea020001dc7e"), + Position.KEY_BATTERY_LEVEL, 45); + + verifyAttribute(decoder, binary( + "7E02000079013653183645009E00000000000C0C030158BF0006C926670000004000CE22120904274201040000005DBC3244524956494E47204C4943454E53452454455354244D522E0000000000000000000000000000000000000000000000000000BD0F323431393939393935383030313030E3060000050500007102000C30011F310108987E"), + "driver", "DRIVING LICENSE$TEST$MR."); + + verifyAttribute(decoder, binary( + "7e55019c3b8571110003399a07032310302029538631031015370500001a0c000000265700440001233703080000001001020202000a0a04028f000af401040c06ff98ffa8007e707e"), + "tilt", "[-104,-88,126]"); + + verifyPosition(decoder, binary( + "7e0900001f4f07788ef87d000cf0230223150215010203013800000c000b029dc58c04b99b60230223171822507e")); + + verifyPosition(decoder, binary( + "7e0200004107904226220608ca0000010000000010031dac0d004864f30000000000002212291003220104000179a7300107310100eb17000300e151000300e304000b00d801041edf340000306b007e")); + + verifyPosition(decoder, binary( + "7E020000FE069223000241002E00000000000C0003015A98F806C8A1260013000000E622082617464401040000017F02020001030200002504000000002A0200002B0400000000300117310112E306000005890000F3B4000202000000030202F2000402375F00050400000000000602000C000702000C000801320009020072000B020035000C020050000D020176000E0122000F018A00501B4C46504D34414350584731413337303937000000000000000000000052040000000C01000200010101040000000001020200010103040000000101040203E7010C02000E010D020000010E02059B010F020072011002387001110200000112020000011302000001140200000116020000D17E")); + + verifyPositions(decoder, binary( + "7E021001A2010036526447000A3B00000000000000010158F52206C916B0000000000000161118110121661D019431000B0000CF47006931000B000058A4006930000B0000882400693B00000000000000010158F52206C916B0000000000000161118110122661D019431000B0000CF47006931000B000058A4006930000B0000882400693B00000000000000010158F52206C916B0000000000000161118110123661D019431000B0000CF47006931000B000058A4006930000B0000882400693B00000000000000010158F52206C916B0000000000000161118110124661D019431000B0000CF47006931000B000058A4006930000B0000882400691C00000000000000010158F52206C916B00000000000001611181101253B00000000000000010158F52206C916B0000000000000161118110126661D019431000B0000CF47006931000B000058A4006930000B0000882400693B00000000000000010158F52206C916B0000000000000161118110127661D019431000B0000CF47006931000B000058A4006930000B0000882400691C00000000000000010158F52206C916B0000000000000161118110128F57E")); + + verifyAttribute(decoder, binary( + "7e02000042012291302260198f00000000800c012300d2651605ff3188001e0000000022102510310003020000a70400000000ac040000012ce5020003e60b03bc572900ce2eef183200e7030000005c7e"), + Position.PREFIX_TEMP + 3, -17.094117647058823); + + verifyAttribute(decoder, binary( + "7E0200008201215233475100030000000000000003015A7F6106CF8CEC003D0000000021071311481901040000005630011931011AE10200755D3D0601CC0024990A7dA0032301CC002499099B2941FC01CC002499099B29430B01CC0024990A7dA0290601CC0024990A7dA015FD01CC0026220994506BFFFE157C010400000001F10C000000000000000000000000997E"), + Position.KEY_ALARM, Position.ALARM_ACCELERATION); + + verifyAttribute(decoder, binary( + "7e020000340551231425560568000000000400000201618a9706c320e100410000002722060816261501040000015d300115310105eb0a000300e164000300e301957e"), + Position.KEY_BATTERY_LEVEL, 100); verifyNull(decoder, buffer( "(794104004140,1,001,BASE,2,TIME)")); + verifyAttribute(decoder, binary( + "7E02000053200002604323004800000000000C00000158B91406CB7007006B00000000220512101138010400000F2803020000250400000000300114310100E30100D50E0100026043232795980277530030E50400000000E7020FC8E8020E83AB7E"), + "lock1Locked", false); + + verifyPosition(decoder, binary( + "7E0900005A4E5DE66FBA2200C4F02204280610090002010D052B0150052C0400129009052D016B052E014F05300231BA053502000005360203B8053802000005390200AD053D0201EA05440150054604009899D90545040000001E000C00030160A85C06D1C1389C7E")); + verifyNull(decoder, binary( "7E01000021013345678906000F002C012F373031313142534A2D4D3742203030303030303001D4C1423838383838B47E")); verifyAttribute(decoder, binary( + "7E020000480123456789010013000000800000000301597BC506CBFF6600EB00000155210726203531010400000000EB24000C00B28986047701207027150200060089FFFFEFFF0004002D0E10000600C5FFFFFFEF697E"), + Position.KEY_ALARM, Position.ALARM_LOW_BATTERY); + + verifyAttribute(decoder, binary( "7e0200008e01917159043700b300000000800000030158990606ca0fd7000400000000211129111705010400000000cc14383938363037423831303230393031363239363830010d8001aa81021388820200858301148401aa8502189b8601338702007e8801338901148a0200998b1131323334353637383941424344454647488c04000200a88d0200828e0114a00b50353338662c5530323966037e"), Position.KEY_DTCS, "P538f U029f"); diff --git a/src/test/java/org/traccar/protocol/HuabaoProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/HuabaoProtocolEncoderTest.java index 1cb273c2b..87cc2b12e 100644 --- a/src/test/java/org/traccar/protocol/HuabaoProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/HuabaoProtocolEncoderTest.java @@ -1,17 +1,17 @@ package org.traccar.protocol; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; public class HuabaoProtocolEncoderTest extends ProtocolTest { - @Ignore + @Disabled @Test public void testEncode() throws Exception { - var encoder = new HuabaoProtocolEncoder(null); + var encoder = inject(new HuabaoProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/HunterProProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HunterProProtocolDecoderTest.java index 81fdae95c..e932ff7e9 100644 --- a/src/test/java/org/traccar/protocol/HunterProProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/HunterProProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class HunterProProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class HunterProProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new HunterProProtocolDecoder(null); + var decoder = inject(new HunterProProtocolDecoder(null)); verifyPosition(decoder, text( ">0002<$GPRMC,170559.000,A,0328.3045,N,07630.0735,W,0.73,266.16,200816,,,A77, s000078015180\",0MD")); diff --git a/src/test/java/org/traccar/protocol/IdplProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/IdplProtocolDecoderTest.java index a5141c389..da4e25fda 100644 --- a/src/test/java/org/traccar/protocol/IdplProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/IdplProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class IdplProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class IdplProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new IdplProtocolDecoder(null); + var decoder = inject(new IdplProtocolDecoder(null)); verifyPosition(decoder, text( "*ID1,863071011086474,210314,153218,A,1831.4577,N,07351.1433,E,0.79,240.64,9,20,A,1,4.20,0,1,01,1,0,0,A01,R,935D#"), diff --git a/src/test/java/org/traccar/protocol/IntellitracProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/IntellitracProtocolDecoderTest.java index ee3a25cbe..8de96a890 100644 --- a/src/test/java/org/traccar/protocol/IntellitracProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/IntellitracProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class IntellitracProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class IntellitracProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new IntellitracProtocolDecoder(null); + var decoder = inject(new IntellitracProtocolDecoder(null)); verifyPosition(decoder, text( "359316075744331,20201008181424,12.014662,57.826301,0,76,24,10,997,3,0,0.000,4.208,20201008181424,0")); diff --git a/src/test/java/org/traccar/protocol/IotmProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/IotmProtocolDecoderTest.java index ca72874ef..c4f01fde4 100644 --- a/src/test/java/org/traccar/protocol/IotmProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/IotmProtocolDecoderTest.java @@ -2,7 +2,7 @@ package org.traccar.protocol; import io.netty.handler.codec.mqtt.MqttMessageBuilders; import io.netty.handler.codec.mqtt.MqttQoS; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class IotmProtocolDecoderTest extends ProtocolTest { @@ -10,7 +10,7 @@ public class IotmProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new IotmProtocolDecoder(null); + var decoder = inject(new IotmProtocolDecoder(null)); verifyNull(decoder, MqttMessageBuilders.connect().clientId( "123456789012345").build()); diff --git a/src/test/java/org/traccar/protocol/ItsFrameDecoderTest.java b/src/test/java/org/traccar/protocol/ItsFrameDecoderTest.java index 363185b4c..ebdb6fd0f 100644 --- a/src/test/java/org/traccar/protocol/ItsFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ItsFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ItsFrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class ItsFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new ItsFrameDecoder(); + var decoder = inject(new ItsFrameDecoder()); verifyFrame( binary("242c2c3836383732383033373731373434312c312e3444335f4149533134305f312e302c56455253494f4e312e302c32382e3633333731372c4e2c37372e3232323730322c45"), diff --git a/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java index dfd86969a..86b438c7c 100644 --- a/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,7 @@ public class ItsProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new ItsProtocolDecoder(null); + var decoder = inject(new ItsProtocolDecoder(null)); verifyNull(decoder, text( "$,LGN,MARK,000000000,358980100077446,V0.0.1,AIS140,19.804487,N,75.225876,E,*")); diff --git a/src/test/java/org/traccar/protocol/ItsProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/ItsProtocolEncoderTest.java index 5302709fc..b9bda0c0a 100644 --- a/src/test/java/org/traccar/protocol/ItsProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/ItsProtocolEncoderTest.java @@ -1,17 +1,17 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class ItsProtocolEncoderTest extends ProtocolTest { @Test public void testEncode() throws Exception { - var encoder = new ItsProtocolEncoder(null); + var encoder = inject(new ItsProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/Ivt401ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Ivt401ProtocolDecoderTest.java index 8a71cb4d5..45655898e 100644 --- a/src/test/java/org/traccar/protocol/Ivt401ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Ivt401ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Ivt401ProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class Ivt401ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Ivt401ProtocolDecoder(null); + var decoder = inject(new Ivt401ProtocolDecoder(null)); verifyPosition(decoder, text( "(TLA,356917051007891,190118,090211,+16.986606,+82.242416,0,66,4,13,1,5,000,00,0.0,11.59,8.30,37.77,0.0,1,1.02,0,0,208,0,0,0,0,000000000,0,0,0,0,0,0,0,1,8654604,5,9,114)")); diff --git a/src/test/java/org/traccar/protocol/JidoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/JidoProtocolDecoderTest.java index 9e01d7d68..6b60e19cc 100644 --- a/src/test/java/org/traccar/protocol/JidoProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/JidoProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class JidoProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class JidoProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new JidoProtocolDecoder(null); + var decoder = inject(new JidoProtocolDecoder(null)); verifyPosition(decoder, text( "*12345678910101000,01,A,130517,160435,1820.5845,N,07833.2478,E,20,0,067,045,05,28,26,1,075,Y,1,0000,0000,0000,59")); diff --git a/src/test/java/org/traccar/protocol/JpKorjarProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/JpKorjarProtocolDecoderTest.java index 13137006c..c8e29d2ec 100644 --- a/src/test/java/org/traccar/protocol/JpKorjarProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/JpKorjarProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class JpKorjarProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class JpKorjarProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new JpKorjarProtocolDecoder(null); + var decoder = inject(new JpKorjarProtocolDecoder(null)); verifyPosition(decoder, text( "KORJAR.PL,329587014519383,160910144240,52.247254N,021.013375E,0.00,1,F:4.18V,1 260 01 794B 3517,")); diff --git a/src/test/java/org/traccar/protocol/JsonFrameDecoderTest.java b/src/test/java/org/traccar/protocol/JsonFrameDecoderTest.java index 42777e419..f40bcd720 100644 --- a/src/test/java/org/traccar/protocol/JsonFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/JsonFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class JsonFrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class JsonFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new JsonFrameDecoder(); + var decoder = inject(new JsonFrameDecoder()); verifyFrame( binary("7b226465764964223a2243485a4430384b504430323130343235303436222c2264657654797065223a322c226861726456657273696f6e223a224844545456413139222c226d736754797065223a3131302c2270726f746f636f6c56657273696f6e223a225631222c22736f667456657273696f6e223a22332e312e38222c22737769746368436162537461747573223a2231222c2274786e4e6f223a2231363235323132373431353337227d"), diff --git a/src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java index eda97ba2d..2dce2309d 100644 --- a/src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Jt600FrameDecoderTest extends ProtocolTest { @@ -8,7 +8,15 @@ public class Jt600FrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Jt600FrameDecoder(); + var decoder = inject(new Jt600FrameDecoder()); + + verifyFrame( + binary("2460201102320112003401010000000422434199114158229e000000000009000000000010e06400000000000020100e0868817043592664000000000000"), + decoder.decode(null, null, binary("2460201102320112003401010000000422434199114158229e000000000009000000000010e06400000000000020100e0868817043592664000000000000"))); + + verifyFrame( + binary("24657060730131001b13111710361906538525079524797f000000000000000003f300036c"), + decoder.decode(null, null, binary("24657060730131001b13111710361906538525079524797f000000000000000003f300036c"))); verifyFrame( binary("2480413009781914003406102107544354193631006213423b00000000006c070000000020e064f91ea0671d00020f0f0f0f0f0f0f0f0f0f07f100ea0f6e"), diff --git a/src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java index 3bf01c1ae..32213fda1 100644 --- a/src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java @@ -1,15 +1,17 @@ package org.traccar.protocol; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import org.junit.Test; - public class Jt600ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Jt600ProtocolDecoder(null); + var decoder = inject(new Jt600ProtocolDecoder(null)); + + verifyPositions(decoder, binary( + "2480433966040111002718031919195822424550114158888E15A40000F124080000000000F00F110A24991900000DF0C7")); verifyPosition(decoder, buffer( "(8000632862,P45,290322,132412,25.28217,S,57.54683,W,A,0,0,5,0,0000000000,0,0,9,0)")); diff --git a/src/test/java/org/traccar/protocol/Jt600ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Jt600ProtocolEncoderTest.java index 51cefb962..5c8c260b7 100644 --- a/src/test/java/org/traccar/protocol/Jt600ProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/Jt600ProtocolEncoderTest.java @@ -1,11 +1,11 @@ package org.traccar.protocol; -import static org.junit.Assert.assertEquals; - -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class Jt600ProtocolEncoderTest extends ProtocolTest { Jt600ProtocolEncoder encoder = new Jt600ProtocolEncoder(null); Command command = new Command(); diff --git a/src/test/java/org/traccar/protocol/KenjiProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/KenjiProtocolDecoderTest.java index 53ef1d5ca..1425bc066 100755 --- a/src/test/java/org/traccar/protocol/KenjiProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/KenjiProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class KenjiProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class KenjiProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new KenjiProtocolDecoder(null); + var decoder = inject(new KenjiProtocolDecoder(null)); verifyPosition(decoder, text( ">C800000,M005004,O0000,I0002,D124057,A,S3137.2783,W05830.2978,T000.0,H254.3,Y240116,G06*17"), diff --git a/src/test/java/org/traccar/protocol/KhdProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/KhdProtocolDecoderTest.java index 5c2b0732b..7cc65002b 100644 --- a/src/test/java/org/traccar/protocol/KhdProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/KhdProtocolDecoderTest.java @@ -1,14 +1,19 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; +import org.traccar.model.Position; public class KhdProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new KhdProtocolDecoder(null); + var decoder = inject(new KhdProtocolDecoder(null)); + + verifyAttribute(decoder, binary( + "2929a3003e1680ba0a2304180759500000000000000000000000007b00000080001914000000000000000000162001641b0b0000249002bc58030001cc46020000e70d"), + Position.KEY_BATTERY_LEVEL, 100); verifyPosition(decoder, binary( "2929800028258b8c10210731035840031534240542120200000337fb000000ffff5a00000a0000000005005d0d")); diff --git a/src/test/java/org/traccar/protocol/KhdProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/KhdProtocolEncoderTest.java index 287d05fca..1ee652cd9 100644 --- a/src/test/java/org/traccar/protocol/KhdProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/KhdProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; @@ -9,7 +9,7 @@ public class KhdProtocolEncoderTest extends ProtocolTest { @Test public void testEncode() throws Exception { - var encoder = new KhdProtocolEncoder(null); + var encoder = inject(new KhdProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/L100FrameDecoderTest.java b/src/test/java/org/traccar/protocol/L100FrameDecoderTest.java index 45c69105b..a9e0a26fe 100644 --- a/src/test/java/org/traccar/protocol/L100FrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/L100FrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class L100FrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class L100FrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new L100FrameDecoder(); + var decoder = inject(new L100FrameDecoder()); verifyFrame( binary("41544c2c4c2c3836383334353033383137313936332c4e2c3230313231382c3039333031362c412c3032352e3036373134342c4e2c3035352e3134343833332c452c3030302e302c4750532c333933392c3432342c30332c30303430352c303038383334"), diff --git a/src/test/java/org/traccar/protocol/L100ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/L100ProtocolDecoderTest.java index d4bef3885..105e1f3f5 100644 --- a/src/test/java/org/traccar/protocol/L100ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/L100ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class L100ProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class L100ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new L100ProtocolDecoder(null); + var decoder = inject(new L100ProtocolDecoder(null)); verifyPosition(decoder, text( "ATL,NP,868004029750174,$GPRMC,062943,A,2533.6719,N,09154.3203,E,0,179,311218,,,*39,#01111011000000,0,0,0,934.82,27.13,4.0,25,405,755,15af,974b,0,0,0,ATL")); diff --git a/src/test/java/org/traccar/protocol/LacakProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/LacakProtocolDecoderTest.java index c407d1e64..a8afb8573 100644 --- a/src/test/java/org/traccar/protocol/LacakProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/LacakProtocolDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; import io.netty.handler.codec.http.HttpMethod; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class LacakProtocolDecoderTest extends ProtocolTest { @@ -9,7 +9,7 @@ public class LacakProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new LacakProtocolDecoder(null); + var decoder = inject(new LacakProtocolDecoder(null)); verifyPosition(decoder, request(HttpMethod.POST, "/", buffer("{\"location\":{\"event\":\"motionchange\",\"is_moving\":false,\"uuid\":\"0e9a2473-a9a7-4c00-997b-fb97d2154e75\",\"timestamp\":\"2021-07-21T08:06:34.444Z\",\"odometer\":0,\"coords\":{\"latitude\":-6.1148096,\"longitude\":106.6837015,\"accuracy\":3.8,\"speed\":18.67,\"speed_accuracy\":0.26,\"heading\":63,\"heading_accuracy\":0.28,\"altitude\":35.7,\"altitude_accuracy\":3.8},\"activity\":{\"type\":\"still\",\"confidence\":100},\"battery\":{\"is_charging\":false,\"level\":0.79},\"extras\":{}},\"device_id\":\"8737767034\"}"))); diff --git a/src/test/java/org/traccar/protocol/LaipacProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/LaipacProtocolDecoderTest.java index 1d5819603..4df486d56 100644 --- a/src/test/java/org/traccar/protocol/LaipacProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/LaipacProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class LaipacProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class LaipacProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new LaipacProtocolDecoder(null); + var decoder = inject(new LaipacProtocolDecoder(null)); verifyPosition(decoder, text( "$AVRMC,80006405,212645,r,3013.9938,N,08133.3998,W,0.00,0.00,010317,a,4076,0,1,0,0,53170583,310260*78")); diff --git a/src/test/java/org/traccar/protocol/LeafSpyProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/LeafSpyProtocolDecoderTest.java index aed82eb41..dcff59f0c 100644 --- a/src/test/java/org/traccar/protocol/LeafSpyProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/LeafSpyProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class LeafSpyProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class LeafSpyProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new LeafSpyProtocolDecoder(null); + var decoder = inject(new LeafSpyProtocolDecoder(null)); verifyNull(decoder, request( "/?Lat=60.0&Long=30.0")); diff --git a/src/test/java/org/traccar/protocol/M2cProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/M2cProtocolDecoderTest.java index ede056f96..0ac1ea92f 100644 --- a/src/test/java/org/traccar/protocol/M2cProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/M2cProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class M2cProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class M2cProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new M2cProtocolDecoder(null); + var decoder = inject(new M2cProtocolDecoder(null)); verifyPositions(decoder, text( "[#M2C,2020,P1.B1.H3.F9.R1,102,864547034433966,1,L,0,20,171221,062016,28.647552,77.192841,0,0,0.0,0,0,64,255,11983,0,0,0,0.0,0,0,0,404,4,1F6,4D77,31,0*7524\r\n", diff --git a/src/test/java/org/traccar/protocol/M2mProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/M2mProtocolDecoderTest.java index f318dcfef..c4db6945d 100644 --- a/src/test/java/org/traccar/protocol/M2mProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/M2mProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class M2mProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class M2mProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new M2mProtocolDecoder(null); + var decoder = inject(new M2mProtocolDecoder(null)); verifyNull(decoder, binary( "235A3C2A2624215C287D70212A21254C7C6421220B0B0B")); diff --git a/src/test/java/org/traccar/protocol/MaestroProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MaestroProtocolDecoderTest.java index 19f9b9da7..237ef45b5 100644 --- a/src/test/java/org/traccar/protocol/MaestroProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MaestroProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class MaestroProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class MaestroProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new MaestroProtocolDecoder(null); + var decoder = inject(new MaestroProtocolDecoder(null)); verifyPosition(decoder, text( "@353893040202807,705,UPV-02,1,13.2,17,0,0,16/09/11,11:42:49,0.352705,32.647918,1210.5,0.000000,35.33,11,0.8,0.000,0!\0")); diff --git a/src/test/java/org/traccar/protocol/ManPowerProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ManPowerProtocolDecoderTest.java index 74f3ff1ec..560c7967b 100644 --- a/src/test/java/org/traccar/protocol/ManPowerProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ManPowerProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ManPowerProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class ManPowerProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new ManPowerProtocolDecoder(null); + var decoder = inject(new ManPowerProtocolDecoder(null)); verifyPosition(decoder, text( "simei:352581250259539,,,tracker,51,24,1.73,130426023608,A,3201.5462,N,03452.2975,E,0.01,28B9,1DED,425,01,1x0x0*0x1*60x+2,en-us,"), diff --git a/src/test/java/org/traccar/protocol/Mavlink2ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Mavlink2ProtocolDecoderTest.java index ccddb6a11..add137114 100644 --- a/src/test/java/org/traccar/protocol/Mavlink2ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Mavlink2ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Mavlink2ProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class Mavlink2ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Mavlink2ProtocolDecoder(null); + var decoder = inject(new Mavlink2ProtocolDecoder(null)); verifyAttributes(decoder, binary( "fd1c0000ce01012100004da91f004005d323b89aa30ea6ed070099fb0100f7fffdff0000942c4a88")); diff --git a/src/test/java/org/traccar/protocol/MegastekFrameDecoderTest.java b/src/test/java/org/traccar/protocol/MegastekFrameDecoderTest.java index 19e5cb0ab..996a4c98a 100644 --- a/src/test/java/org/traccar/protocol/MegastekFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MegastekFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class MegastekFrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class MegastekFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new MegastekFrameDecoder(); + var decoder = inject(new MegastekFrameDecoder()); verifyFrame( binary("30313337244d47563030322c3335343535303035303239323636392c4756543930302c522c3134313231352c3033313830342c412c2c532c2c452c30302c30332c30302c332e36372c302e3030302c302e30302c3131372e312c302e302c3531302c31302c2c2c2c303030302c303030302c32322c31322c302c202c202c2c312d312c39382c5057204f4e3b21"), diff --git a/src/test/java/org/traccar/protocol/MegastekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MegastekProtocolDecoderTest.java index 83d62e766..227fb20e0 100644 --- a/src/test/java/org/traccar/protocol/MegastekProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MegastekProtocolDecoderTest.java @@ -1,15 +1,15 @@ package org.traccar.protocol; -import org.junit.Test; -import org.traccar.model.Position; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; +import org.traccar.model.Position; public class MegastekProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new MegastekProtocolDecoder(null); + var decoder = inject(new MegastekProtocolDecoder(null)); verifyPosition(decoder, text( "$MGV002,860719020193193,,S,070521,160748,V,2255.09165,N,11404.01322,E,00,00,00,,,,,,,,,,,,,,,,,,,10,015,Restart;!")); diff --git a/src/test/java/org/traccar/protocol/MeiligaoFrameDecoderTest.java b/src/test/java/org/traccar/protocol/MeiligaoFrameDecoderTest.java index c0e5f1e97..2647c49db 100644 --- a/src/test/java/org/traccar/protocol/MeiligaoFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MeiligaoFrameDecoderTest.java @@ -1,17 +1,17 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; public class MeiligaoFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new MeiligaoFrameDecoder(); + var decoder = inject(new MeiligaoFrameDecoder()); assertNull( decoder.decode(null, null, binary("00"))); diff --git a/src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java index 087701be4..8074636a3 100644 --- a/src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,7 @@ public class MeiligaoProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new MeiligaoProtocolDecoder(null); + var decoder = inject(new MeiligaoProtocolDecoder(null)); verifyAttribute(decoder, binary( "2424008f142180340967ff99553033333233302e3030302c412c313531362e383039392c4e2c31303435322e383835352c452c302e30302c33332c3038313232302c2c2a33367c302e387c3132337c323130307c303030302c303030302c303230452c303241417c30323038303030353038394530304531434638347c31437c31373243353832437c3042a8060d0a"), diff --git a/src/test/java/org/traccar/protocol/MeiligaoProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/MeiligaoProtocolEncoderTest.java index 9c50d972a..f62a9f722 100644 --- a/src/test/java/org/traccar/protocol/MeiligaoProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/MeiligaoProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; @@ -9,7 +9,7 @@ public class MeiligaoProtocolEncoderTest extends ProtocolTest { @Test public void testEncode() throws Exception { - var encoder = new MeiligaoProtocolEncoder(null); + var encoder = inject(new MeiligaoProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); @@ -36,6 +36,10 @@ public class MeiligaoProtocolEncoderTest extends ProtocolTest { verifyCommand(encoder, command, binary("4040001312345678901234410603e87bb00d0a")); + command.setType(Command.TYPE_ENGINE_STOP); + + verifyCommand(encoder, command, binary("4040001212345678901234411501fd460d0a")); + } } diff --git a/src/test/java/org/traccar/protocol/MeitrackFrameDecoderTest.java b/src/test/java/org/traccar/protocol/MeitrackFrameDecoderTest.java index 38d0f2f92..f86b676a6 100644 --- a/src/test/java/org/traccar/protocol/MeitrackFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MeitrackFrameDecoderTest.java @@ -1,16 +1,16 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class MeitrackFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new MeitrackFrameDecoder(); + var decoder = inject(new MeitrackFrameDecoder()); assertEquals( binary("24244e3132372c3836333037313031333830333036362c4141412c33352c2d312e3330323638302c33362e3835323133352c3135303430393231313032362c412c392c302c302e312c302c352c313635332c343039362c33323634382c3633397c30327c313030347c3930432c303030302c307c307c307c3346467c3330302c2a37430d0a"), diff --git a/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java index 1c90468bd..8d2aee501 100644 --- a/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,36 @@ public class MeitrackProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new MeitrackProtocolDecoder(null); + var decoder = inject(new MeitrackProtocolDecoder(null)); + + verifyAttribute(decoder, binary( + "2424683136342C3836363334343035333039353238322C4343452C000000000100820018000505000600070B14001500080800000900000A00000B00001606001A0000402300FE90000006022E79570103E55CCC0604E1FDB32B0CC32C00000D58EB02001C01000000050E0CCC010000B627BF11000000004B1001010D475052532847534D2039303029FEA50601FFFFFF7FFFFEA80701010258023800FEB20501010000002A41360D0A"), + "battery2Level", 88); + + verifyAttribute(decoder, binary( + "2424593434312c3836353431333035303839313733372c4343452c00000000030088001800050501061607191400150008080000098e000a05000b0c001608001a0000402300fe9000000602c3fe5ffe03e22a1f0904e6688d2b0cd94002000d5f6f03001c01000000050e0cf901010032700298c80899ff4b16010113464444204c5445284c54452042414e44203329fea50601ffffff7ffffea807024d0000000000feb205010000000083001700050501061607191400150008080000098e000a05000b0c001608001a0000405100fe9000000502c3fe5ffe03e22a1f0904e6688d2b0cd94002000d606f0300050e0cf901010032700298c80899ff4b16010113464444204c5445284c54452042414e44203329fea50601ffffff7ffffea807024d0000000000feb205010000000088001800050501061607151400150008080000098e000a05000b0c001607001a0000402300fe9000000602c3fe5ffe03e22a1f0904f0688d2b0cd94002000d696f03001c01000000050e0cf901010032700298c80897ff4b16010113464444204c5445284c54452042414e44203329fea50601ffffff7ffffea807024d0000000000feb20501000000002a36320d0a"), + Position.KEY_BATTERY_LEVEL, 77); + + verifyAttribute(decoder, binary( + "24245b3131342c3836343630363034343939333938372c4343452c0000000001005000130006012305000600070f1b004702060800000900000a00000b0000199d011a00000602d179570103b25ccc0604cf04862b0cc65b01000da4090d001c01000000010e0ccc010000b627be11000000002a41300d0a"), + Position.KEY_LOCK, true); + + verifyAttribute(decoder, buffer( + "$$u28,864606044993987,D82,0*D6"), + Position.KEY_RESULT, "D82,0"); + + verifyPositions(decoder, binary( + "24245B3139312C3836343630363034343939333938372C4343452C010000000200500013000601250500060007111B00470206080000093E000AE7030B0000199E011A850306028D7A570103F35ACC0604F9D06C2B0CB92E00000D3FA40C00250CA2B900010E0CCC010000B6276313000000004B00120006012A0500060007111B00470206080000093E000AE7030B0000199E011A7C0305028D7A570103F35ACC0604F9D06C2B0CB92E00000D3FA40C00010E0CCC010000B6276313000000002A31340D0A")); + + verifyPositions(decoder, binary( + "24246a3138312c3836343238313034313930383330332c4343452c00000000010093001f000505000600070714001502090800000900000a00000b0000160a001706001904001ad90440230006023279570103305ccc0604f536492b0c510300000d495701001c014000000b0e0ccc010000922781abb90c00002a030034212b03008b082c030053082d03009e082e030034212f030034213003003421310300342149090400000000000000004b07010104574946492a36310d0a")); + + verifyPositions(decoder, binary( + "2424423233322c3836323039303035303030323831332c4343452c0400000003004400110004050006000700fe6962060800000900000a00000b00001aef044023000602d65fbcfd03173b9c0804cc76ae2a0c14ae1b000d00aa0d001c01000000014b030101003f00100004050006000700fe695f060800000900000a00000b00001aea044016000502d65fbcfd03173b9c0804cf76ae2a0c14ae1b000d03aa0d00014b030101003f00100004050006000700fe695f060800000900000a00000b00001aed044001000502d65fbcfd03173b9c0804d076ae2a0c14ae1b000d04aa0d00014b030101002a30460d0a")); + + verifyAttribute(decoder, buffer( + "$$F160,861412043027965,AAA,22,45.499458,-82.493581,220718171428,V,0,0,0,0,0.0,0,227940,119812,302|220|D8D6|086E1B2B,0000,0000|0000|0000|0191|0573,,,3,,002134,0,0*FA"), + Position.KEY_POWER, 13.95); verifyPositions(decoder, binary( "2424423233392c3836323039303035303030373436352c4343452c0100000003004300130006050006000700140015801b00080800000900000a00000b0000165105198d011a630540160005024c5e910103590bfe0204922153290c6b2501000dd5b50200004300130006050006000700140015011b00080800000900000a00000b0000165005198d011a630540010005024c5e910103590bfe0204932153290c6b2501000dd6b50200004300130006050006000700140015011b00080800000900000a00000b0000165205198d011a630540230005024c5e910103590bfe0204942153290c6b2501000dd7b50200002a43330d0a")); @@ -122,6 +151,9 @@ public class MeitrackProtocolDecoderTest extends ProtocolTest { verifyPositions(decoder, binary( "2424473937302c3336393830303031333436303637342c4343432c020134005b000000010ce304035db9e000ec6f591a000013000000000c001801edb70200c96d0100e60001004838576501000300a101c20400000000010ce304035db9e000ee6f591a000013000000000c001801edb70200ca6d0100e60001004838576501000300a101c20400000000010ce304035db9e000ef6f591a000013000000000c001801edb70200cc6d0100e60001004838576501000300a101c20400000000020ce304035db9e000f76f591a000016000000000c001801edb70200d36d0100e60001004838576502000300a101bf04000000000a0ce304035db9e000f76f591a000016000000000c001801edb70200d46d0100e60001004838576500000300a101bf0400000000020ce304035db9e000fb6f591a000016000000000c001801edb70200d86d0100e60001004838576502000300a101760400000000180ce304035db9e000fc6f591a0000120000000000008c00edb70200d96d0100e60001004838576502000300a10176040000000019b1e2040323b9e0000b70591a0105150600bb0012002901edb70200e76d0100e60001004838576502000300a2017005000000002023e304031fb9e0001070591a010615070027010d001601fcb70200ec6d0100e60001004838576502000300a201800500000000201fe3040302b9e0001170591a010615090019010d001501feb70200ed6d0100e60001004838576502000300a2018005000000002018e30403dcb8e0001270591a0106150b0011010d00150100b80200ee6d0100e60001004838576502000300a2018005000000002036e3040345b8e0001570591a0107150b002d010b0013010ab80200f16d0100e60001004838576502000300a2018005000000002053e3040326b8e0001670591a0107150d0041010b0013010eb80200f26d0100e60001004838576502000300a2018005000000002070e3040310b8e0001770591a0107150e004f010b00130111b80200f36d0100e60001004838576502000300a2018005000000002095e3040306b8e0001870591a0107150d005a010b00140115b80200f46d0100e60001004838576502000300a20180050000000020b3e3040305b8e0001970591a0107150b0060010b00140118b80200f56d0100e60001004838576502000300a20183050000000020cfe3040308b8e0001a70591a0107150b0066010b0014011bb80200f66d0100e60001004838576502000300a20183050000000020eee304030cb8e0001b70591a0106170b0004000d0014011eb80200f76d0100e60001004838576502000300a2018305000000002a62350d0a")); + verifyNull(decoder, buffer( + "$$z27,861451040910625,AAC,1*D3")); + } } diff --git a/src/test/java/org/traccar/protocol/MeitrackProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/MeitrackProtocolEncoderTest.java index 64fc7e17b..ac9854b8e 100644 --- a/src/test/java/org/traccar/protocol/MeitrackProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/MeitrackProtocolEncoderTest.java @@ -1,17 +1,17 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class MeitrackProtocolEncoderTest extends ProtocolTest { @Test public void testEncode() throws Exception { - var encoder = new MeitrackProtocolEncoder(null); + var encoder = inject(new MeitrackProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java index ca8b67a46..3f790d2f9 100644 --- a/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,7 @@ public class MictrackProtocolDecoderTest extends ProtocolTest { @Test public void testDecodeStandard() throws Exception { - var decoder = new MictrackProtocolDecoder(null); + var decoder = inject(new MictrackProtocolDecoder(null)); verifyAttributes(decoder, text( "MT;5;867035041396795;Y1;220111085741+test,8c:53:c3:db:e7:26,-58,jiuide-842,80:26:89:f0:5e:4f,-74,jiu2ide 403,94:e4:4b:0a:31:08,-75,jiu3ide,7a:91:e9:50:26:0b,-85,CNet-9rNe,78:91:e9:40:26:0b,-87+0+4092+1")); @@ -48,7 +48,7 @@ public class MictrackProtocolDecoderTest extends ProtocolTest { @Test public void testDecodeLowAltitude() throws Exception { - var decoder = new MictrackProtocolDecoder(null); + var decoder = inject(new MictrackProtocolDecoder(null)); verifyPositions(decoder, text( "861836051888035$162835.00,A,4139.6460,N,07009.7239,W,,41.53,-25.8,220621")); diff --git a/src/test/java/org/traccar/protocol/MilesmateProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MilesmateProtocolDecoderTest.java index 69fd82886..d14c020d4 100644 --- a/src/test/java/org/traccar/protocol/MilesmateProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MilesmateProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class MilesmateProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class MilesmateProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new MilesmateProtocolDecoder(null); + var decoder = inject(new MilesmateProtocolDecoder(null)); verifyPosition(decoder, text( "ApiString={A:861359037373030,B:09.8,C:00.0,D:083506,E:2838.5529N,F:07717.8049E,G:000.00,H:170918,I:G,J:00004100,K:0000000A,L:1234,M:126.86}")); diff --git a/src/test/java/org/traccar/protocol/MiniFinderProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MiniFinderProtocolDecoderTest.java index 1a9756226..712d59dc9 100644 --- a/src/test/java/org/traccar/protocol/MiniFinderProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MiniFinderProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,7 @@ public class MiniFinderProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new MiniFinderProtocolDecoder(null); + var decoder = inject(new MiniFinderProtocolDecoder(null)); verifyNull(decoder, text( "!1,867273023933661,V07S.5701.1621,100")); @@ -21,6 +21,10 @@ public class MiniFinderProtocolDecoderTest extends ProtocolTest { "!1,123456789012345")); verifyAttribute(decoder, text( + "!4,10,040123,,,1.0,110,0,0S,33"), + "phone1", "040123"); + + verifyAttribute(decoder, text( "!5,17,V,50"), Position.KEY_BATTERY_LEVEL, 50); diff --git a/src/test/java/org/traccar/protocol/MiniFinderProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/MiniFinderProtocolEncoderTest.java index 652490b72..f61779a38 100644 --- a/src/test/java/org/traccar/protocol/MiniFinderProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/MiniFinderProtocolEncoderTest.java @@ -1,17 +1,17 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class MiniFinderProtocolEncoderTest extends ProtocolTest { @Test public void testEncode() throws Exception { - var encoder = new MiniFinderProtocolEncoder(null); + var encoder = inject(new MiniFinderProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java index ab23f277a..64d245a8e 100644 --- a/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java @@ -1,22 +1,38 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; +import org.traccar.model.Position; public class Minifinder2ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Minifinder2ProtocolDecoder(null); + var decoder = inject(new Minifinder2ProtocolDecoder(null)); + + verifyAttribute(decoder, binary( + "ab101c00d6f61e000110013836333932313033393939363038300937efd201640c000000"), + "barkCount", 12L); + + verifyAttribute(decoder, binary( + "ab00030008c700007f0100"), + Position.KEY_RESULT, "0"); + + verifyAttribute(decoder, binary( + "ab102600080f1400011001383633393231303339393833343736092429b347633003a96409020000008027b34763"), + "bark", true); + + verifyPositions(decoder, binary( + "AB103D0035A700000110013836373733303035333430333237390924AC5783620103C250162030CC5F0D5002FB432D00AF005A3158006D0A00000B0931EC5783620A000000")); verifyPositions(decoder, binary( "ab10350015ae59010110013836333932313033333836353231360924723a12610042535a182ac0f6b4f2923100c900af02215c2b9bfb5461736b4c4d53")); - verifyNull(decoder, binary( + verifyPositions(decoder, false, binary( "ab10150076f1320003100133353534363530373130323933303602105a")); - verifyNull(decoder, binary( + verifyPositions(decoder, false, binary( "AB101400594A01000310013836333932323033343437333734350112")); verifyPositions(decoder, binary( diff --git a/src/test/java/org/traccar/protocol/Minifinder2ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Minifinder2ProtocolEncoderTest.java new file mode 100644 index 000000000..ef6ff6dd9 --- /dev/null +++ b/src/test/java/org/traccar/protocol/Minifinder2ProtocolEncoderTest.java @@ -0,0 +1,29 @@ +package org.traccar.protocol; + +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; +import org.traccar.model.Command; +import org.traccar.model.Device; + +import static org.mockito.Mockito.when; + +public class Minifinder2ProtocolEncoderTest extends ProtocolTest { + + @Test + public void testEncodeNano() throws Exception { + + var encoder = inject(new Minifinder2ProtocolEncoder(null)); + + var device = encoder.getCacheManager().getObject(Device.class, 1); + when(device.getModel()).thenReturn("Nano"); + + Command command = new Command(); + command.setDeviceId(1); + command.setType(Command.TYPE_FIRMWARE_UPDATE); + command.set(Command.KEY_DATA, "https://example.com"); + + verifyCommand(encoder, command, binary("ab00160059d2010004143068747470733a2f2f6578616d706c652e636f6d")); + + } + +} diff --git a/src/test/java/org/traccar/protocol/MobilogixProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MobilogixProtocolDecoderTest.java index ddfa6ad8b..b6cc2ed77 100644 --- a/src/test/java/org/traccar/protocol/MobilogixProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MobilogixProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,7 @@ public class MobilogixProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new MobilogixProtocolDecoder(null); + var decoder = inject(new MobilogixProtocolDecoder(null)); verifyAttributes(decoder, text( "[2021-08-20 19:27:14,T14,1,V1.3.5,201909000982,53,12.18")); diff --git a/src/test/java/org/traccar/protocol/MoovboxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MoovboxProtocolDecoderTest.java index af19b8222..8e51271b6 100644 --- a/src/test/java/org/traccar/protocol/MoovboxProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MoovboxProtocolDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; import io.netty.handler.codec.http.HttpMethod; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class MoovboxProtocolDecoderTest extends ProtocolTest { @@ -9,7 +9,7 @@ public class MoovboxProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new MoovboxProtocolDecoder(null); + var decoder = inject(new MoovboxProtocolDecoder(null)); verifyPositions(decoder, request(HttpMethod.POST, "/", buffer("<gps id=\"911\">\n<coordinates><coordinate>\n<fix>3</fix>\n<time>1597580050</time>\n<latitude>100.726257</latitude>\n<longitude>13.821351</longitude>\n<altitude>9.500000</altitude>\n<climb>0.000000</climb>\n<speed>0.064000</speed>\n<separation>-27.300000</separation>\n<track>0.000000</track>\n<satellites>9</satellites>\n</coordinate></coordinates>\n</gps>"))); diff --git a/src/test/java/org/traccar/protocol/MotorProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MotorProtocolDecoderTest.java index bd4a97ef4..9de24d87f 100644 --- a/src/test/java/org/traccar/protocol/MotorProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MotorProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class MotorProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class MotorProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new MotorProtocolDecoder(null); + var decoder = inject(new MotorProtocolDecoder(null)); verifyPosition(decoder, text( "341200007E7E00007E7E020301803955352401161766210162090501010108191625132655351234567F12345F")); diff --git a/src/test/java/org/traccar/protocol/MtxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MtxProtocolDecoderTest.java index 28b5d3be0..86a72cc2d 100644 --- a/src/test/java/org/traccar/protocol/MtxProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MtxProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class MtxProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class MtxProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new MtxProtocolDecoder(null); + var decoder = inject(new MtxProtocolDecoder(null)); verifyPosition(decoder, text( "#MTX,353815011138124,20101226,195550,41.6296399,002.3611174,000,035,000000.00,X,X,1111,000,0,0")); diff --git a/src/test/java/org/traccar/protocol/MxtProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MxtProtocolDecoderTest.java index 301b6102b..c1f02f0ae 100644 --- a/src/test/java/org/traccar/protocol/MxtProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MxtProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class MxtProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class MxtProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new MxtProtocolDecoder(null); + var decoder = inject(new MxtProtocolDecoder(null)); verifyPosition(decoder, binary( "01a631a7627b00087dc41c40850006aab70affecdf23fd32200080000600000000000000000000001b2ff03b1bb9c4c60214f40100050000006c2d0000f427600051051101de0704")); diff --git a/src/test/java/org/traccar/protocol/NavigilProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NavigilProtocolDecoderTest.java index 60d88999e..5b5865855 100644 --- a/src/test/java/org/traccar/protocol/NavigilProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NavigilProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class NavigilProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class NavigilProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new NavigilProtocolDecoder(null); + var decoder = inject(new NavigilProtocolDecoder(null)); verifyNull(decoder, binary( "01004300040020000000f60203080200e7cd0f510c0000003b00000000000000")); diff --git a/src/test/java/org/traccar/protocol/NavisFrameDecoderTest.java b/src/test/java/org/traccar/protocol/NavisFrameDecoderTest.java index 0ebfeacd2..8678a55ba 100644 --- a/src/test/java/org/traccar/protocol/NavisFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NavisFrameDecoderTest.java @@ -1,9 +1,8 @@ package org.traccar.protocol; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import org.junit.Test; - public class NavisFrameDecoderTest extends ProtocolTest { @Test diff --git a/src/test/java/org/traccar/protocol/NavisProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NavisProtocolDecoderTest.java index b1282cac7..56f5a4c9f 100644 --- a/src/test/java/org/traccar/protocol/NavisProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NavisProtocolDecoderTest.java @@ -1,15 +1,14 @@ package org.traccar.protocol; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import org.junit.Test; - public class NavisProtocolDecoderTest extends ProtocolTest { @Test public void testDecodeNtcb() throws Exception { - var decoder = new NavisProtocolDecoder(null); + var decoder = inject(new NavisProtocolDecoder(null)); verifyNull(decoder, binary( "404E5443010000007B000000130044342A3E533A383631373835303035323035303739")); @@ -41,7 +40,7 @@ public class NavisProtocolDecoderTest extends ProtocolTest { @Test public void testDecodeFlex10() throws Exception { - var decoder = new NavisProtocolDecoder(null); + var decoder = inject(new NavisProtocolDecoder(null)); verifyNull(decoder, binary( "404e544301000000c9b5f602130046c52a3e533a383639363936303439373232383235")); @@ -60,7 +59,7 @@ public class NavisProtocolDecoderTest extends ProtocolTest { @Test public void testDecodeFlex20() throws Exception { - var decoder = new NavisProtocolDecoder(null); + var decoder = inject(new NavisProtocolDecoder(null)); verifyNull(decoder, binary( "404e544301000000a9eef602130043fb2a3e533a383639363936303439373337333835")); diff --git a/src/test/java/org/traccar/protocol/NavisetFrameDecoderTest.java b/src/test/java/org/traccar/protocol/NavisetFrameDecoderTest.java index e73c173b7..8f25c2127 100644 --- a/src/test/java/org/traccar/protocol/NavisetFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NavisetFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class NavisetFrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class NavisetFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new NavisetFrameDecoder(); + var decoder = inject(new NavisetFrameDecoder()); verifyFrame( binary("1310e4073836383230343030353935383436362a060716"), diff --git a/src/test/java/org/traccar/protocol/NavisetProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NavisetProtocolDecoderTest.java index df4e57e8d..7722560ca 100644 --- a/src/test/java/org/traccar/protocol/NavisetProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NavisetProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class NavisetProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class NavisetProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new NavisetProtocolDecoder(null); + var decoder = inject(new NavisetProtocolDecoder(null)); verifyNull(decoder, binary( "1310e4073836383230343030353935383436362a060716")); diff --git a/src/test/java/org/traccar/protocol/NavtelecomFrameDecoderTest.java b/src/test/java/org/traccar/protocol/NavtelecomFrameDecoderTest.java index 562b220d4..1509472a7 100644 --- a/src/test/java/org/traccar/protocol/NavtelecomFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NavtelecomFrameDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class NavtelecomFrameDecoderTest extends ProtocolTest { @@ -9,7 +9,7 @@ public class NavtelecomFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new NavtelecomFrameDecoder(); + var decoder = inject(new NavtelecomFrameDecoder()); verifyFrame( binary("404e5443010000000000000013004e452a3e533a383636373935303331343130363839"), @@ -24,11 +24,11 @@ public class NavtelecomFrameDecoderTest extends ProtocolTest { } - @Ignore + @Disabled @Test public void testDecodeFull() throws Exception { - var decoder = new NavtelecomFrameDecoder(); + var decoder = inject(new NavtelecomFrameDecoder()); verifyFrame( binary("404e5443010000000000000013004e452a3e533a383636373935303331343130363839"), diff --git a/src/test/java/org/traccar/protocol/NavtelecomProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NavtelecomProtocolDecoderTest.java index fd22049fc..d715ea596 100644 --- a/src/test/java/org/traccar/protocol/NavtelecomProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NavtelecomProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class NavtelecomProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class NavtelecomProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new NavtelecomProtocolDecoder(null); + var decoder = inject(new NavtelecomProtocolDecoder(null)); verifyNull(decoder, binary( "404e5443010000000000000013004e452a3e533a383636373935303331343130363839")); diff --git a/src/test/java/org/traccar/protocol/NdtpV6ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NdtpV6ProtocolDecoderTest.java new file mode 100644 index 000000000..67e88cb5a --- /dev/null +++ b/src/test/java/org/traccar/protocol/NdtpV6ProtocolDecoderTest.java @@ -0,0 +1,18 @@ +package org.traccar.protocol; + +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; + +public class NdtpV6ProtocolDecoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + var decoder = inject(new NdtpV6ProtocolDecoder(null)); + + verifyAttributes(decoder, binary( + "7e7e3b000200334202000000000000000064000100000000000600020002034f0c0200000400000000000033353135313330353131393430353532353030323632373237343836363500")); + + } + +} diff --git a/src/test/java/org/traccar/protocol/NeosProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NeosProtocolDecoderTest.java index b77bdf658..841315895 100644 --- a/src/test/java/org/traccar/protocol/NeosProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NeosProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class NeosProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class NeosProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new NeosProtocolDecoder(null); + var decoder = inject(new NeosProtocolDecoder(null)); verifyPosition(decoder, text( ">12345678,1,1,070201,144111,W05829.2613,S3435.2313,,00,034,25,00,126-000,0,3,11111111*2d!\r\n")); diff --git a/src/test/java/org/traccar/protocol/NetProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NetProtocolDecoderTest.java index 9ab4aea4f..d208b10c5 100644 --- a/src/test/java/org/traccar/protocol/NetProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NetProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class NetProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class NetProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new NetProtocolDecoder(null); + var decoder = inject(new NetProtocolDecoder(null)); verifyPosition(decoder, text( "@L03686090604017761712271020161807037078881037233751000000010F850036980A4000")); diff --git a/src/test/java/org/traccar/protocol/NiotProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NiotProtocolDecoderTest.java index 7707094a5..8fa74abd0 100644 --- a/src/test/java/org/traccar/protocol/NiotProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NiotProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class NiotProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class NiotProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new NiotProtocolDecoder(null); + var decoder = inject(new NiotProtocolDecoder(null)); verifyPosition(decoder, binary( "585880004c08675430347318522007161451458024b28003f566ee00000328f8000748217ffc500729007a280000000000160001383932353430323130363431363738373136323100050002004e00570d"), diff --git a/src/test/java/org/traccar/protocol/NoranProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NoranProtocolDecoderTest.java index a30847160..3a3461d43 100644 --- a/src/test/java/org/traccar/protocol/NoranProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NoranProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class NoranProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class NoranProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new NoranProtocolDecoder(null); + var decoder = inject(new NoranProtocolDecoder(null)); verifyNull(decoder, binary( "0d0a2a4b57000d000080010d0a")); diff --git a/src/test/java/org/traccar/protocol/NoranProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/NoranProtocolEncoderTest.java index 76486d024..ddcc02418 100644 --- a/src/test/java/org/traccar/protocol/NoranProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/NoranProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; @@ -9,7 +9,7 @@ public class NoranProtocolEncoderTest extends ProtocolTest { @Test public void testEncode() throws Exception { - var encoder = new NoranProtocolEncoder(null); + var encoder = inject(new NoranProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/NvsFrameDecoderTest.java b/src/test/java/org/traccar/protocol/NvsFrameDecoderTest.java index 76c7cafb9..d4dcdcc60 100644 --- a/src/test/java/org/traccar/protocol/NvsFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NvsFrameDecoderTest.java @@ -1,16 +1,16 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class NvsFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new NvsFrameDecoder(); + var decoder = inject(new NvsFrameDecoder()); assertEquals( binary("0012333537303430303630303137383234312e38"), diff --git a/src/test/java/org/traccar/protocol/NvsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NvsProtocolDecoderTest.java index ed4008d47..0c8b41e4a 100644 --- a/src/test/java/org/traccar/protocol/NvsProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NvsProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class NvsProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class NvsProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new NvsProtocolDecoder(null); + var decoder = inject(new NvsProtocolDecoder(null)); verifyNull(decoder, binary( "0012333537303430303630303137383234312e38")); diff --git a/src/test/java/org/traccar/protocol/NyitechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NyitechProtocolDecoderTest.java index 81de06f89..9dcfe8a78 100644 --- a/src/test/java/org/traccar/protocol/NyitechProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NyitechProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class NyitechProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class NyitechProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new NyitechProtocolDecoder(null); + var decoder = inject(new NyitechProtocolDecoder(null)); verifyPosition(decoder, binary( "4040690030313436383230303238373201201c0c12031a308080801c0c12031a3007d67e7e08aceb841002000000ae08000000000000000000000000001e002900f0ffdd002700f2ffe0002700f2ffe1002400f0ffdf002400f3ffe3008a00ffff01010000a9c70d0a")); diff --git a/src/test/java/org/traccar/protocol/ObdDongleProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ObdDongleProtocolDecoderTest.java index 08ebf9995..53d910ddb 100644 --- a/src/test/java/org/traccar/protocol/ObdDongleProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ObdDongleProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ObdDongleProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class ObdDongleProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new ObdDongleProtocolDecoder(null); + var decoder = inject(new ObdDongleProtocolDecoder(null)); verifyNull(decoder, binary( "55550003383634383637303232353131303135010009010011023402010201ABAAAA")); diff --git a/src/test/java/org/traccar/protocol/OigoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OigoProtocolDecoderTest.java index 023158f21..4541bf9c0 100644 --- a/src/test/java/org/traccar/protocol/OigoProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/OigoProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class OigoProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class OigoProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new OigoProtocolDecoder(null); + var decoder = inject(new OigoProtocolDecoder(null)); verifyPosition(decoder, binary( "7e002e000000146310002523830400001bfb000369150f310c0591594d062ac0c0141508011303cd63101604fd00000000")); diff --git a/src/test/java/org/traccar/protocol/OkoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OkoProtocolDecoderTest.java index 0df537642..d3bd4fde4 100644 --- a/src/test/java/org/traccar/protocol/OkoProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/OkoProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class OkoProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class OkoProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new OkoProtocolDecoder(null); + var decoder = inject(new OkoProtocolDecoder(null)); verifyPosition(decoder, text( "{861001001012919,090745,A,4944.302,N,02353.366,E,0.0,225,251120,7,0.27,F9,11.3,1}")); diff --git a/src/test/java/org/traccar/protocol/OmnicommFrameDecoderTest.java b/src/test/java/org/traccar/protocol/OmnicommFrameDecoderTest.java index c8bbf399a..1a4365f3c 100644 --- a/src/test/java/org/traccar/protocol/OmnicommFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/OmnicommFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class OmnicommFrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class OmnicommFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new OmnicommFrameDecoder(); + var decoder = inject(new OmnicommFrameDecoder()); verifyFrame( binary("c08600004566"), diff --git a/src/test/java/org/traccar/protocol/OmnicommProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OmnicommProtocolDecoderTest.java index 76b476fc2..9f509718a 100644 --- a/src/test/java/org/traccar/protocol/OmnicommProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/OmnicommProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class OmnicommProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class OmnicommProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new OmnicommProtocolDecoder(null); + var decoder = inject(new OmnicommProtocolDecoder(null)); verifyNull(decoder, binary( "c080080061a61915340100001dec")); diff --git a/src/test/java/org/traccar/protocol/OpenGtsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OpenGtsProtocolDecoderTest.java index 9fbd79cbf..e2db193d1 100644 --- a/src/test/java/org/traccar/protocol/OpenGtsProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/OpenGtsProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class OpenGtsProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class OpenGtsProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new OpenGtsProtocolDecoder(null); + var decoder = inject(new OpenGtsProtocolDecoder(null)); verifyPosition(decoder, request( "/?id=999000000000003&gprmc=$GPRMC,082202.0,A,5006.747329,N,01416.512315,E,0.0,,131018,1.2,E,A*2E")); diff --git a/src/test/java/org/traccar/protocol/OrbcommProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OrbcommProtocolDecoderTest.java index 7d3068c02..21ccfa56d 100644 --- a/src/test/java/org/traccar/protocol/OrbcommProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/OrbcommProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class OrbcommProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class OrbcommProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new OrbcommProtocolDecoder(null); + var decoder = inject(new OrbcommProtocolDecoder(null)); verifyNull(decoder, response( buffer("{\"ErrorID\":0,\"NextStartUTC\":\"\",\"Messages\":null}"))); diff --git a/src/test/java/org/traccar/protocol/OrionProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OrionProtocolDecoderTest.java index 7368a9d4e..5308568fd 100644 --- a/src/test/java/org/traccar/protocol/OrionProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/OrionProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class OrionProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class OrionProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new OrionProtocolDecoder(null); + var decoder = inject(new OrionProtocolDecoder(null)); verifyPositions(decoder, binary( "5057000137bf6236235a0331b5c6e402a3b5ecff5102980003000e0c1d172936080e0c1d172936b03b01000882050000008e080000000000008c0300940500000084030085030003067600900113150000000000000000000000000000000000000004a4c8")); diff --git a/src/test/java/org/traccar/protocol/OsmAndProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OsmAndProtocolDecoderTest.java index a87b45ec4..c779e4c6e 100644 --- a/src/test/java/org/traccar/protocol/OsmAndProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/OsmAndProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class OsmAndProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class OsmAndProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new OsmAndProtocolDecoder(null); + var decoder = inject(new OsmAndProtocolDecoder(null)); verifyNotNull(decoder, request( "/?id=123456×tamp=1377177267&cell=257,02,16,2224&cell=257,02,16,2223,-90&wifi=00-14-22-01-23-45,-80&wifi=00-1C-B3-09-85-15,-70")); diff --git a/src/test/java/org/traccar/protocol/OutsafeProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OutsafeProtocolDecoderTest.java index 1194f7970..7347da0fb 100644 --- a/src/test/java/org/traccar/protocol/OutsafeProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/OutsafeProtocolDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; import io.netty.handler.codec.http.HttpMethod; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class OutsafeProtocolDecoderTest extends ProtocolTest { @@ -9,7 +9,7 @@ public class OutsafeProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new OutsafeProtocolDecoder(null); + var decoder = inject(new OutsafeProtocolDecoder(null)); verifyPosition(decoder, request(HttpMethod.POST, "/", buffer("{\"device\":\"865303040103725\",\"owner\":\"\",\"data\":{\"cmd\":\"\",\"ms1\":-1,\"ms2\":-1,\"ms3\":0,\"ms4\":0,\"observation\":\"\",\"content\":null},\"time\":1589277568,\"origin\":\"mqgatte\",\"latitude\":19.346855,\"longitude\":-99.29587,\"altitude\":2757,\"heading\":0,\"rssi\":0}"))); diff --git a/src/test/java/org/traccar/protocol/OwnTracksProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OwnTracksProtocolDecoderTest.java index 55b48fb05..ba0eaec01 100644 --- a/src/test/java/org/traccar/protocol/OwnTracksProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/OwnTracksProtocolDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; import io.netty.handler.codec.http.HttpMethod; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class OwnTracksProtocolDecoderTest extends ProtocolTest { @@ -9,7 +9,7 @@ public class OwnTracksProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new OwnTracksProtocolDecoder(null); + var decoder = inject(new OwnTracksProtocolDecoder(null)); verifyPosition(decoder, request(HttpMethod.POST, "/", buffer("{\"_type\":\"location\",\"acc\":15,\"alt\":440,\"batt\":46,\"conn\":\"w\",\"lat\":46.0681247,\"lon\":11.1512805,\"t\":\"u\",\"tid\":\"5t\",\"tst\":1551874878,\"vac\":2,\"vel\":0}"))); diff --git a/src/test/java/org/traccar/protocol/PacificTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PacificTrackProtocolDecoderTest.java index edf508314..4ae0e6d54 100644 --- a/src/test/java/org/traccar/protocol/PacificTrackProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/PacificTrackProtocolDecoderTest.java @@ -1,10 +1,10 @@ package org.traccar.protocol; import io.netty.buffer.Unpooled; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class PacificTrackProtocolDecoderTest extends ProtocolTest { @@ -22,7 +22,7 @@ public class PacificTrackProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new PacificTrackProtocolDecoder(null); + var decoder = inject(new PacificTrackProtocolDecoder(null)); verifyAttributes(decoder, binary( "FB80019702808835275309000091108181B2C08F0143000E10000000010000001400010192DF0143288063810A8202835584D285B486E68780882D89C38A788BCE8C3A8D3C8E418F809073A008ACA16600A225A0C0000F4240C10003DF2CC200004E20C3004428C0C4000008C6C5000316A4E011314334424A57464758444C35333137373302A086AB569DFE110E02A8811203FF81000190820100")); diff --git a/src/test/java/org/traccar/protocol/PathAwayProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PathAwayProtocolDecoderTest.java index e4c9bf449..afd131e95 100644 --- a/src/test/java/org/traccar/protocol/PathAwayProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/PathAwayProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class PathAwayProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class PathAwayProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new PathAwayProtocolDecoder(null); + var decoder = inject(new PathAwayProtocolDecoder(null)); verifyPosition(decoder, request( "?UserName=name&Password=pass&LOC=$PWS,1,\"Roger\",,,100107,122846,45.317270,-79.642219,45.00,42,1,\"Comment\",0*58")); diff --git a/src/test/java/org/traccar/protocol/PiligrimProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PiligrimProtocolDecoderTest.java index b39060420..cb101fa5f 100644 --- a/src/test/java/org/traccar/protocol/PiligrimProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/PiligrimProtocolDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; import io.netty.handler.codec.http.HttpMethod; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class PiligrimProtocolDecoderTest extends ProtocolTest { @@ -9,12 +9,16 @@ public class PiligrimProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new PiligrimProtocolDecoder(null); + var decoder = inject(new PiligrimProtocolDecoder(null)); verifyPositions(decoder, request(HttpMethod.POST, "/bingps?imei=868204005544720&csq=18&vout=00&vin=4050&dataid=00000000", binary("fff2200d4110061a32354f3422310062000a0005173b0000a101000300005e00fff2200d4110100932354f2b22310042000b000e173b00009f01000700006000"))); + verifyPosition(decoder, request(HttpMethod.POST, + "/push.do", + buffer("&phoneNumber=%2B+78000000000&message=ALARM KEY; $GPRMC,180752.000,A,5314.0857,N,03421.8173,E,0.00,104.74,220722,,,A,V* 29,05; GSM: 250-01 0b54-0519,1c30,3e96,3ebe,412e 25; S; Batt: 405,M"))); + } } diff --git a/src/test/java/org/traccar/protocol/PluginProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PluginProtocolDecoderTest.java index 2d134d967..59f624eaa 100644 --- a/src/test/java/org/traccar/protocol/PluginProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/PluginProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,7 @@ public class PluginProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new PluginProtocolDecoder(null); + var decoder = inject(new PluginProtocolDecoder(null)); verifyPosition(decoder, text( "$$STATUS,000000900005,20210521111252,27.171105,-25.600934,62.0,323,0,-1,2,0.000,2147489155,0.00,0,0,0.0,0.0,0,0,0,0,0,0,0,0,0")); diff --git a/src/test/java/org/traccar/protocol/PolteProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PolteProtocolDecoderTest.java index 592267b9e..67bac7823 100644 --- a/src/test/java/org/traccar/protocol/PolteProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/PolteProtocolDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; import io.netty.handler.codec.http.HttpMethod; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class PolteProtocolDecoderTest extends ProtocolTest { @@ -9,7 +9,7 @@ public class PolteProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new PolteProtocolDecoder(null); + var decoder = inject(new PolteProtocolDecoder(null)); verifyPosition(decoder, request(HttpMethod.POST, "/", buffer("{\"_id\":\"5f75cf7b02c5023bfc0beaf7\",\"location\":{\"LocationMetrics\":{\"EnvironmentDensity\":1,\"LocationType\":2,\"carrierInfo\":{\"aux\":{\"PLMN\":\"310410\",\"country\":\"United States\",\"name\":\"ATT Wireless Inc\"},\"crs\":{\"PLMN\":\"310410\",\"country\":\"United States\",\"name\":\"ATT Wireless Inc\"}},\"hdop\":1850000,\"leversion\":\"2.2.18-20200729T140651\",\"towercount\":1},\"altitude\":0.0011297669261693954,\"confidence\":783.7972188868215,\"detected_at\":1601556342,\"latitude\":29.77368956725161,\"longitude\":-98.26530342694024,\"towerDB\":\"default\",\"ueToken\":\"ALT12503DE04336CB2E3A4A113FCDE05DF05A6F\",\"uid\":\"WZuDMv5Je\"},\"report\":{\"battery\":{\"count\":555,\"level\":100,\"voltage\":3.52},\"event\":3,\"time\":\"2020-10-01T12:45:48.207Z\"},\"time\":\"2020-10-01T12:45:42Z\",\"ueToken\":\"ALT12503DE04336CB2E3A4A113FCDE05DF05A6F\",\"uid\":\"WZuDMv5Je\"}"))); diff --git a/src/test/java/org/traccar/protocol/PortmanProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PortmanProtocolDecoderTest.java index 37798d960..c5d57aa25 100644 --- a/src/test/java/org/traccar/protocol/PortmanProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/PortmanProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class PortmanProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class PortmanProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new PortmanProtocolDecoder(null); + var decoder = inject(new PortmanProtocolDecoder(null)); verifyPosition(decoder, text( "$EXT,P0RTMANGRANT,A,210609201710,N0951.6879W08357.0129,0,0,NA,NA,11,25,174700.25,NA,01820000,108")); diff --git a/src/test/java/org/traccar/protocol/PortmanProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/PortmanProtocolEncoderTest.java index 61f6c4a4e..41e78cf6c 100644 --- a/src/test/java/org/traccar/protocol/PortmanProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/PortmanProtocolEncoderTest.java @@ -1,17 +1,17 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class PortmanProtocolEncoderTest extends ProtocolTest { @Test - public void testEncodeEngineStop() { + public void testEncodeEngineStop() throws Exception { - var encoder = new PortmanProtocolEncoder(null); + var encoder = inject(new PortmanProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); @@ -22,9 +22,9 @@ public class PortmanProtocolEncoderTest extends ProtocolTest { } @Test - public void testEncodeEngineResume() { + public void testEncodeEngineResume() throws Exception { - var encoder = new PortmanProtocolEncoder(null); + var encoder = inject(new PortmanProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/PretraceProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PretraceProtocolDecoderTest.java index 8a4f257f5..ca4d5de07 100644 --- a/src/test/java/org/traccar/protocol/PretraceProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/PretraceProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class PretraceProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class PretraceProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new PretraceProtocolDecoder(null); + var decoder = inject(new PretraceProtocolDecoder(null)); verifyPosition(decoder, text( "(867967021915915U1110A1701201500102238.1700N11401.9324E000264000000000009001790000000,&P11A4,F1050^47")); diff --git a/src/test/java/org/traccar/protocol/PretraceProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/PretraceProtocolEncoderTest.java index 403c89e9e..da18441f5 100644 --- a/src/test/java/org/traccar/protocol/PretraceProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/PretraceProtocolEncoderTest.java @@ -1,17 +1,17 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class PretraceProtocolEncoderTest extends ProtocolTest { @Test public void testEncodePositionPeriodic() throws Exception { - var encoder = new PretraceProtocolEncoder(null); + var encoder = inject(new PretraceProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); @@ -25,7 +25,7 @@ public class PretraceProtocolEncoderTest extends ProtocolTest { @Test public void testEncodeCustom() throws Exception { - var encoder = new PretraceProtocolEncoder(null); + var encoder = inject(new PretraceProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/PricolProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PricolProtocolDecoderTest.java index a9373e22e..6988f2f72 100644 --- a/src/test/java/org/traccar/protocol/PricolProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/PricolProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class PricolProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class PricolProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new PricolProtocolDecoder(null); + var decoder = inject(new PricolProtocolDecoder(null)); verifyPosition(decoder, binary( "3c5052493030303350020000011402110b222b0455152e4e001de819ca450000000000000003820249000000000000000000000000000000000000000040003e")); diff --git a/src/test/java/org/traccar/protocol/ProgressProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ProgressProtocolDecoderTest.java index 6e59f2876..ff205ce35 100644 --- a/src/test/java/org/traccar/protocol/ProgressProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ProgressProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ProgressProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class ProgressProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new ProgressProtocolDecoder(null); + var decoder = inject(new ProgressProtocolDecoder(null)); verifyNull(decoder, binary( "020037000100000003003131310f003335343836383035313339303036320f00323530303136333832383531353535010000000100000000000000e6bb97b6")); diff --git a/src/test/java/org/traccar/protocol/PstFrameDecoderTest.java b/src/test/java/org/traccar/protocol/PstFrameDecoderTest.java index 96993b97b..45960d783 100644 --- a/src/test/java/org/traccar/protocol/PstFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/PstFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class PstFrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class PstFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new PstFrameDecoder(); + var decoder = inject(new PstFrameDecoder()); verifyFrame( binary("2fafac5a050f0000e0022fafac5a01891e882bbfdd06dd577c9865620a0efe524c419f940b6710f5ba0c86e5868ffc97c77eaaf166a31dba63f9894e98a91b9486c94e79ce537359737a5e9385431a590eb20b5115a2b7939e4e66ae"), diff --git a/src/test/java/org/traccar/protocol/PstFrameEncoderTest.java b/src/test/java/org/traccar/protocol/PstFrameEncoderTest.java index bc458c398..bbc17dbea 100644 --- a/src/test/java/org/traccar/protocol/PstFrameEncoderTest.java +++ b/src/test/java/org/traccar/protocol/PstFrameEncoderTest.java @@ -2,7 +2,7 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class PstFrameEncoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/PstProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PstProtocolDecoderTest.java index 445c333c1..1fe7d7da4 100644 --- a/src/test/java/org/traccar/protocol/PstProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/PstProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class PstProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class PstProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new PstProtocolDecoder(null); + var decoder = inject(new PstProtocolDecoder(null)); verifyPosition(decoder, binary( "2faf97de06000024db0551380cbb08070b040000015a0c09b50177e5100a1822da0d010d0f0451380628101451380cc384b800488a84036901b202d3010001061103ffff00150203523687")); diff --git a/src/test/java/org/traccar/protocol/PstProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/PstProtocolEncoderTest.java index abcfa29ec..b6530d815 100644 --- a/src/test/java/org/traccar/protocol/PstProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/PstProtocolEncoderTest.java @@ -1,15 +1,15 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; public class PstProtocolEncoderTest extends ProtocolTest { @Test - public void testEncodeEngineStop() { + public void testEncodeEngineStop() throws Exception { - var encoder = new PstProtocolEncoder(null); + var encoder = inject(new PstProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); @@ -20,9 +20,9 @@ public class PstProtocolEncoderTest extends ProtocolTest { } @Test - public void testEncodeEngineResume() { + public void testEncodeEngineResume() throws Exception { - var encoder = new PstProtocolEncoder(null); + var encoder = inject(new PstProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/Pt215ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Pt215ProtocolDecoderTest.java index 24cfd316a..234aff97b 100644 --- a/src/test/java/org/traccar/protocol/Pt215ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Pt215ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Pt215ProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class Pt215ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Pt215ProtocolDecoder(null); + var decoder = inject(new Pt215ProtocolDecoder(null)); verifyNull(decoder, binary( "58580d010359339075435451010d0a")); diff --git a/src/test/java/org/traccar/protocol/Pt3000ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Pt3000ProtocolDecoderTest.java index 44f57601c..b731b82ad 100644 --- a/src/test/java/org/traccar/protocol/Pt3000ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Pt3000ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Pt3000ProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class Pt3000ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Pt3000ProtocolDecoder(null); + var decoder = inject(new Pt3000ProtocolDecoder(null)); verifyPosition(decoder, text( "%356939010012099,$GPRMC,124945.752,A,4436.6245,N,01054.4634,E,0.11,358.52,060408,,,A,+393334347445,N028d"), diff --git a/src/test/java/org/traccar/protocol/Pt502FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Pt502FrameDecoderTest.java index 2559ad145..f007dbb18 100644 --- a/src/test/java/org/traccar/protocol/Pt502FrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Pt502FrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Pt502FrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class Pt502FrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Pt502FrameDecoder(); + var decoder = inject(new Pt502FrameDecoder()); verifyFrame( binary("24504844302c3936302cffd8ffdb008400140e0f120f0d14121012171514181e32211e1c1c1e3d2c2e243249404c4b47404645505a736250556d5645466488656d777b8182814e608d978c7d96737e817c011517171e1a1e3b21213b7c5346537c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7cffc000110800f0014003012100021101031101ffdd0004000affc401a20000010501010101010100000000000000000102030405060708090a0b100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9fa0100030101010101010101010000000000000102030405060708090a0b1100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00e5292800ef450020a2800a2801d49400b450014b40052e2800a69340094a05007fffd0e5d14b10055b51b00c76a00527273494005250014500251400525001450015347c25003a928010d25007ffd1e52909a00290d0014b40052d0014500145002e297b50018a280109a6d002d2e2803fffd2e7a04da3777a94fbd0025140052500145002514005250014940054e381400b494008690d007fffd3e4f345001486800a5a005a2800a2801680280168a002909e280100cd028016a48937bfb5007fffd4c5038a42280128a004a280128a003ad2500251400945002a8cb0a9a80133450026692803ffd5e4e8a004a2801694500145002d18a005c5140052e280109a69a0029680140abb147b139eb401ffd6c62290d00251400949400114940052500252d002525003e31c93525002521a004a4a00ffd7e4a8a00281400a29d40094b40053ba500252d0018a31400d3cd250018cd2d005ab58777ccdd074ab645007ffd0c72290d00348a2801280"), diff --git a/src/test/java/org/traccar/protocol/Pt502ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Pt502ProtocolDecoderTest.java index a68471c95..2fc9c8073 100644 --- a/src/test/java/org/traccar/protocol/Pt502ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Pt502ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,7 @@ public class Pt502ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Pt502ProtocolDecoder(null); + var decoder = inject(new Pt502ProtocolDecoder(null)); verifyNull(decoder, binary( "24504844302c3936302cffd8ffdb008400140e0f120f0d14121012171514181e32211e1c1c1e3d2c2e243249404c4b47404645505a736250556d5645466488656d777b8182814e608d978c7d96737e817c011517171e1a1e3b21213b7c5346537c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7cffc000110800f0014003012100021101031101ffdd0004000affc401a20000010501010101010100000000000000000102030405060708090a0b100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9fa0100030101010101010101010000000000000102030405060708090a0b1100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00e5292800ef450020a2800a2801d49400b450014b40052e2800a69340094a05007fffd0e5d14b10055b51b00c76a00527273494005250014500251400525001450015347c25003a928010d25007ffd1e52909a00290d0014b40052d0014500145002e297b50018a280109a6d002d2e2803fffd2e7a04da3777a94fbd0025140052500145002514005250014940054e381400b494008690d007fffd3e4f345001486800a5a005a2800a2801680280168a002909e280100cd028016a48937bfb5007fffd4c5038a42280128a004a280128a003ad2500251400945002a8cb0a9a80133450026692803ffd5e4e8a004a2801694500145002d18a005c5140052e280109a69a0029680140abb147b139eb401ffd6c62290d00251400949400114940052500252d002525003e31c93525002521a004a4a00ffd7e4a8a00281400a29d40094b40053ba500252d0018a31400d3cd250018cd2d005ab58777ccdd074ab645007ffd0c72290d00348a2801280")); diff --git a/src/test/java/org/traccar/protocol/Pt502ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Pt502ProtocolEncoderTest.java index 62b83c61c..98fe147b2 100644 --- a/src/test/java/org/traccar/protocol/Pt502ProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/Pt502ProtocolEncoderTest.java @@ -1,17 +1,17 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class Pt502ProtocolEncoderTest extends ProtocolTest { @Test public void testEncodeCustom() throws Exception { - var encoder = new Pt502ProtocolEncoder(null); + var encoder = inject(new Pt502ProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); @@ -25,7 +25,7 @@ public class Pt502ProtocolEncoderTest extends ProtocolTest { @Test public void testEncodeOutputControl() throws Exception { - var encoder = new Pt502ProtocolEncoder(null); + var encoder = inject(new Pt502ProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); @@ -40,7 +40,7 @@ public class Pt502ProtocolEncoderTest extends ProtocolTest { @Test public void testEncodeTimezone() throws Exception { - var encoder = new Pt502ProtocolEncoder(null); + var encoder = inject(new Pt502ProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); @@ -55,7 +55,7 @@ public class Pt502ProtocolEncoderTest extends ProtocolTest { @Test public void testEncodeAlarmSpeed() throws Exception { - var encoder = new Pt502ProtocolEncoder(null); + var encoder = inject(new Pt502ProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/Pt60ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Pt60ProtocolDecoderTest.java index ad987240c..5dca8a0c6 100644 --- a/src/test/java/org/traccar/protocol/Pt60ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Pt60ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Pt60ProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class Pt60ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Pt60ProtocolDecoder(null); + var decoder = inject(new Pt60ProtocolDecoder(null)); verifyNull(decoder, text( "@B#@|01|006|864891030184954|9425010450971470|20181213093127|2|1|")); diff --git a/src/test/java/org/traccar/protocol/R12wProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/R12wProtocolDecoderTest.java index acb7277b5..4fbc4938e 100644 --- a/src/test/java/org/traccar/protocol/R12wProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/R12wProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class R12wProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class R12wProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new R12wProtocolDecoder(null); + var decoder = inject(new R12wProtocolDecoder(null)); verifyNull(decoder, text( "$HX,0001,860721009104316,e92c,933402042499509,55792760080,12345678,01,a8d940a9,#,50,")); diff --git a/src/test/java/org/traccar/protocol/RaceDynamicsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RaceDynamicsProtocolDecoderTest.java index 22902079a..c8ed2269d 100644 --- a/src/test/java/org/traccar/protocol/RaceDynamicsProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/RaceDynamicsProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class RaceDynamicsProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class RaceDynamicsProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new RaceDynamicsProtocolDecoder(null); + var decoder = inject(new RaceDynamicsProtocolDecoder(null)); verifyNull(decoder, text( "$GPRMC,12,260819,100708,862549040661129,")); diff --git a/src/test/java/org/traccar/protocol/RadarProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RadarProtocolDecoderTest.java index be1e4de0b..81fdcfd0b 100644 --- a/src/test/java/org/traccar/protocol/RadarProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/RadarProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class RadarProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class RadarProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new RadarProtocolDecoder(null); + var decoder = inject(new RadarProtocolDecoder(null)); verifyPositions(decoder, binary( "361800011459015cb497554c01c101ff003500050038000207ff831c04c01f1c00555cb464895cb46487ff7f04eafeffdbd80000079402ead0000000110000000000120d2aff150000000000000002000a00050002436c61726f0000000000008b00000003764500037653005207ff831c04c01f1c00555cb4648a5cb46489ff7f04eafeffdbd80000079402ead0000000010000000000120e00ff150000000000000002000800060002436c61726f0000000000008d00000003764600037654000207ff831c04c01f1c00555cb464d85cb464d7ff7f04eafeffdbd80000079402ead0000000110000000000120e2aff150000000000000002000700070003436c61726f0000000000008d000000037694000376a2005207ff831c04c01f1c00555cb464d95cb464d9ff7f04eafeffdbd80000079402eac0000000010000000000120e00ff150000000000000002000700070003436c61726f0000000000008d000000037695000376a3000207ff831c04c01f1c00555cb465065cb46504ff7f04eafeffdbd80000079402ead0000000110000000000120e2aff150000000000000000000500060005436c61726f0000000000008d0000000376c2000376d07ed7")); diff --git a/src/test/java/org/traccar/protocol/RaveonProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RaveonProtocolDecoderTest.java index 1b111ee5d..b951ef7b4 100644 --- a/src/test/java/org/traccar/protocol/RaveonProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/RaveonProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class RaveonProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class RaveonProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new RaveonProtocolDecoder(null); + var decoder = inject(new RaveonProtocolDecoder(null)); verifyPosition(decoder, text( "$PRAVE,0001,0001,3308.9051,-11713.1164,195348,1,10,168,31,13.3,3,-83,0,0,,1003.4*66")); diff --git a/src/test/java/org/traccar/protocol/RecodaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RecodaProtocolDecoderTest.java index 803cfb48a..defc5e8f9 100644 --- a/src/test/java/org/traccar/protocol/RecodaProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/RecodaProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class RecodaProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class RecodaProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new RecodaProtocolDecoder(null); + var decoder = inject(new RecodaProtocolDecoder(null)); verifyNull(decoder, binary( "01100020480000000300000030393535360000000000000001000000303030303000000000000000000000000000000000000000006100004531313037353500ffffffffffff0000")); diff --git a/src/test/java/org/traccar/protocol/RetranslatorProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RetranslatorProtocolDecoderTest.java index 4af77cfbf..d27979d1b 100644 --- a/src/test/java/org/traccar/protocol/RetranslatorProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/RetranslatorProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class RetranslatorProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class RetranslatorProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new RetranslatorProtocolDecoder(null); + var decoder = inject(new RetranslatorProtocolDecoder(null)); verifyPosition(decoder, binary( "74000000333533393736303133343435343835004B0BFB70000000030BBB000000270102706F73696E666F00A027AFDF5D9848403AC7253383DD4B400000000000805A40003601460B0BBB0000001200047077725F657874002B8716D9CE973B400BBB00000011010361766C5F696E707574730000000001")); diff --git a/src/test/java/org/traccar/protocol/RfTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RfTrackProtocolDecoderTest.java new file mode 100644 index 000000000..8073c5459 --- /dev/null +++ b/src/test/java/org/traccar/protocol/RfTrackProtocolDecoderTest.java @@ -0,0 +1,19 @@ +package org.traccar.protocol; + +import io.netty.handler.codec.http.HttpMethod; +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; + +public class RfTrackProtocolDecoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + var decoder = inject(new RfTrackProtocolDecoder(null)); + + verifyPosition(decoder, request(HttpMethod.POST, "/deviceDataUpload.do", + buffer("gsm={\"n\":0,\"b\":[{\"l\":6166,\"b\":19,\"c\":21423},{\"l\":6166,\"b\":18,\"c\":21416},{\"l\":6166,\"b\":17,\"c\":21383},{\"l\":6166,\"b\":13,\"c\":21422},{\"l\":6166,\"b\":13,\"c\":21435},{\"l\":6169,\"b\":11,\"c\":21311}],\"c\":460}&wifi=[{\"l\":-49,\"t\":\"lianqin20\",\"m\":\"30-B4-9E-DD-F8-2D\"}]&mt=1073709094&i=358477047125172&gps={\"a\":30.0,\"y\":31.251563,\"s\":4,\"t\":1589764496654,\"z\":0.0,\"x\":121.360346}&dbm=-53&td=1589720501123&rc=0&bar=1001.85065&u_ids=[8441644.1,53036.1]&t=1589764500713&bat=12380&v=T2.142.2_2.0_R03&i_ids=[4247328.1,53036.1,10522408.1]&idt=140736414711817&id=39163"))); + + } + +} diff --git a/src/test/java/org/traccar/protocol/RitiProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RitiProtocolDecoderTest.java index e0890a5fc..7fb5f6335 100644 --- a/src/test/java/org/traccar/protocol/RitiProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/RitiProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class RitiProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class RitiProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new RitiProtocolDecoder(null); + var decoder = inject(new RitiProtocolDecoder(null)); verifyPosition(decoder, binary( "3b28a2a2056315316d4000008100000000000000005f710000244750524d432c3138303535332e3030302c412c353532342e383437312c4e2c30313133342e313837382c452c302e30302c2c3032313231332c2c2c412a37340d0a00000000000000000000000000000000040404")); diff --git a/src/test/java/org/traccar/protocol/RoboTrackFrameDecoderTest.java b/src/test/java/org/traccar/protocol/RoboTrackFrameDecoderTest.java index eaf83458d..f54cf8fcc 100644 --- a/src/test/java/org/traccar/protocol/RoboTrackFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/RoboTrackFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class RoboTrackFrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class RoboTrackFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new RoboTrackFrameDecoder(); + var decoder = inject(new RoboTrackFrameDecoder()); verifyFrame( binary("00524f424f545241434b00000000000000383638323034303032323533343136313233343536373839303132312e313261000000312e353761000000312e3030000000003e"), diff --git a/src/test/java/org/traccar/protocol/RoboTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RoboTrackProtocolDecoderTest.java index 40218efdb..5a83ae1b6 100644 --- a/src/test/java/org/traccar/protocol/RoboTrackProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/RoboTrackProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class RoboTrackProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class RoboTrackProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new RoboTrackProtocolDecoder(null); + var decoder = inject(new RoboTrackProtocolDecoder(null)); verifyNull(decoder, binary( "00524f424f545241434b00000000000000383638323034303032323533343136313233343536373839303132312e313261000000312e353761000000312e3030000000003e")); diff --git a/src/test/java/org/traccar/protocol/RstProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RstProtocolDecoderTest.java index 71313e449..13095765b 100644 --- a/src/test/java/org/traccar/protocol/RstProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/RstProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,7 @@ public class RstProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new RstProtocolDecoder(null); + var decoder = inject(new RstProtocolDecoder(null)); verifyNull(decoder, text( "RST;A;RST-MINIv2;V7.04;008051261;124;29;04-04-2021 17:27:26;04-04-2021 17:27:26;-1.280811;-47.931755;7353;79;1;14;7315;26;10;0;1855;0;0;0;0;5;5;-1.280821;-47.931747;04-04-2021 17:52:23;6;-1.280863;-47.931770;04-04-2021 18:12:19;5;-1.280844;-47.931763;04-04-2021 17:28:02;5;-1.280900;-47.931770;04-04-2021 19:04:27;4;-1.280843;-47.931747;04-04-2021 18:21:45;04-04-2021 19:29:59;04-04-2021 19:29:59;-1.280770;-47.931595;1;15;0;0;0;0;FIM;")); diff --git a/src/test/java/org/traccar/protocol/RuptelaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RuptelaProtocolDecoderTest.java index 64ac6a57e..e98dffdef 100644 --- a/src/test/java/org/traccar/protocol/RuptelaProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/RuptelaProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class RuptelaProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,13 @@ public class RuptelaProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new RuptelaProtocolDecoder(null); + var decoder = inject(new RuptelaProtocolDecoder(null)); + + verifyNull(decoder, binary( + "002e000316d53d58d6020f4573303430302e30332e36382e30340000c2b3090d0e950000827b000003e80000003c003c1681")); + + verifyPositions(decoder, false, binary( + "03fb0003137ca79f856d01011d386d438b000080000000800000008000ffffffffffff070220211bff011d30c50000386d438b000080000000800000008000ffffffffffff0702201f1bff011d30c30000386d43a9000180000000800000008000ffffffffffffad0320211b16ad01011d30950000386d43c7000080000000800000008000ffffffffffff070220211b00011d30a60000386d4403000080000000800000008000ffffffffffff070220221b00011d30ae0000386d443f000080000000800000008000ffffffffffff070220231b00011d30ae000064c692cb000080000000800000008000ffffffffffff070220231b18011d3091000064c69306000080000000800000008000ffffffffffff070220231b14011d30a7000064c69322000180000000800000008000ffffffffffffad0320241b14ad00011d30a3000064c69342000080000000800000008000ffffffffffff070220241b13011d30ad000064c6934a000180000000800000008000ffffffffffffad0320241b10ad01011d30c3000064c6937e000080000000800000008000ffffffffffff070220241b12011d3092000064c6938b000180000000800000008000ffffffffffffad0320241b12ad00011d30bd000064c69395000180000000800000008000ffffffffffffad0320241b10ad01011d30a6000064c693ba000080000000800000008000ffffffffffff070220251b17011d30a2000064c693d4000180000000800000008000ffffffffffffad0320251b17ad00011d30cc000064c693f6000080000000800000008000ffffffffffff070220251b15011d3090000064c69404000180000000800000008000ffffffffffffad0320251b16ad01011d30a9000064c69432000080000000800000008000ffffffffffff070220261b14011d30be000064c6946d000180000000800000008000ffffffffffffad0320261b15ad00011d30b1000064c6946e000080000000800000008000ffffffffffff070220261b15011d3096000064c694aa000080000000800000008000ffffffffffff070220261b15011d30a8000064c694b2000180000000800000008000ffffffffffffad0320261b15ad01011d30a5000064c694e6000080000000800000008000ffffffffffff070220261b17011d309a000064c694f5000180000000800000008000ffffffffffffad0320261b17ad00011d309c000064c694f6000180000000800000008000ffffffffffffad0320261b17ad01011d3099000064c69522000080000000800000008000ffffffffffff070220261b14011d3094000064c6955e000080000000800000008000ffffffffffff070220261b15011d30b2000064c6959a000080000000800000008000ffffffffffff070220261b14011d30970000ad9e")); verifyPositions(decoder, binary( "00800003167d765c155d01000160cd0a310000faae43f7176ee45702332b0c12000006070d05007300cfff260082008600870088000f00d7021100d801c900061d0000c500001e0e988300008900008b000002d0000c9bca720c889a0b047e00000000000000007f0000000000000000800000000000000000810000000000000000a341")); diff --git a/src/test/java/org/traccar/protocol/RuptelaProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/RuptelaProtocolEncoderTest.java index 409cd4da5..bb6c098f0 100644 --- a/src/test/java/org/traccar/protocol/RuptelaProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/RuptelaProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; @@ -9,7 +9,7 @@ public class RuptelaProtocolEncoderTest extends ProtocolTest { @Test public void testEncode() throws Exception { - var encoder = new RuptelaProtocolEncoder(null); + var encoder = inject(new RuptelaProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/S168ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/S168ProtocolDecoderTest.java index ec795c9eb..cebae3ef6 100644 --- a/src/test/java/org/traccar/protocol/S168ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/S168ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class S168ProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class S168ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new S168ProtocolDecoder(null); + var decoder = inject(new S168ProtocolDecoder(null)); verifyAttributes(decoder, text( "S168#861118010104168#00ec#0016#SYNC:0093;STATUS:91,51")); diff --git a/src/test/java/org/traccar/protocol/SabertekFrameDecoderTest.java b/src/test/java/org/traccar/protocol/SabertekFrameDecoderTest.java index ced40acd0..689de29b2 100644 --- a/src/test/java/org/traccar/protocol/SabertekFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SabertekFrameDecoderTest.java @@ -1,16 +1,16 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class SabertekFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new SabertekFrameDecoder(); + var decoder = inject(new SabertekFrameDecoder()); assertEquals( binary("2c3939393939393939392c332c34302c36352c372c302c312c2d32352e3738313636362c32382e3235343730322c34302c3236382c313431342c382c35353632332c"), diff --git a/src/test/java/org/traccar/protocol/SabertekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SabertekProtocolDecoderTest.java index 148695dcd..b55681ffd 100644 --- a/src/test/java/org/traccar/protocol/SabertekProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SabertekProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class SabertekProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class SabertekProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new SabertekProtocolDecoder(null); + var decoder = inject(new SabertekProtocolDecoder(null)); verifyPosition(decoder, text( ",999999999,3,40,65,7,0,1,-25.781666,28.254702,40,268,1414,8,55623,")); diff --git a/src/test/java/org/traccar/protocol/SanavProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SanavProtocolDecoderTest.java index 223f4f12f..5e1a24f3b 100644 --- a/src/test/java/org/traccar/protocol/SanavProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SanavProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class SanavProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class SanavProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new SanavProtocolDecoder(null); + var decoder = inject(new SanavProtocolDecoder(null)); verifyPosition(decoder, text( "imei=353197040023431&rmc=$GPRMC,015258.000,A,2457.8101,N,12125.5393,E,0.00,0.00,210111,,*18,AUTO,0300,2.1,10,466,97,34E7,3391,74,466,9 7,3F2D,3391,65,466,97,39C9,3391,79,466,97,3F2C,3391,81,466,97,0000,00 00,83,466,97,0000,0000,85,466,97,0000,0000,85,1,24")); diff --git a/src/test/java/org/traccar/protocol/SanulProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SanulProtocolDecoderTest.java index 23bd6d80b..f01205155 100644 --- a/src/test/java/org/traccar/protocol/SanulProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SanulProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class SanulProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class SanulProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new SanulProtocolDecoder(null); + var decoder = inject(new SanulProtocolDecoder(null)); verifyNull(decoder, binary( "aa007020000100000000000033353333353830313831353431313700000000000000000000")); diff --git a/src/test/java/org/traccar/protocol/SatsolProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SatsolProtocolDecoderTest.java index dcd51063c..c58971d02 100644 --- a/src/test/java/org/traccar/protocol/SatsolProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SatsolProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class SatsolProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class SatsolProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new SatsolProtocolDecoder(null); + var decoder = inject(new SatsolProtocolDecoder(null)); verifyPositions(decoder, binary( "f0e1bf4cb2ec1600e1005f8791e901000000c959515c2cc24a03aeadcd010e01a800250001090201878e92e901000000cb59515c2dc24a03b8adcd018801a8001d0001080201325993e901000000cc59515c2fc24a03bfadcd01ab01a800220001080201dd8194e901000000cd59515c32c24a03ceadcd015801a8002500010802015f3795e905000900ce59515c32c24a03d8adcd01f600a700250001091401000000000000000000863496e901000000cf59515c34c24a03ddadcd019b00a700280001090201714197e904000600cf59515c34c24a03ddadcd019b00a7002800010a1401becd07001901")); diff --git a/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java index 7e2812714..cc6c17014 100644 --- a/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; import io.netty.handler.codec.http.HttpMethod; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -10,7 +10,7 @@ public class SigfoxProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new SigfoxProtocolDecoder(null); + var decoder = inject(new SigfoxProtocolDecoder(null)); verifyPosition(decoder, request(HttpMethod.POST, "/", buffer("{ \"device\":\"BFE47E\", \"time\":1590497040, \"data\":\"10297eb01e621122070000be\", \"seqNumber\":8, \"deviceTypeId\":\"5ecb8bfac563d620cc9e6798\", \"ack\":false }"))); diff --git a/src/test/java/org/traccar/protocol/SiwiProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SiwiProtocolDecoderTest.java index d264895fc..e599ff36d 100644 --- a/src/test/java/org/traccar/protocol/SiwiProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SiwiProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class SiwiProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class SiwiProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new SiwiProtocolDecoder(null); + var decoder = inject(new SiwiProtocolDecoder(null)); verifyPosition(decoder, text( "$SIWI,868957040831465,44,E,,,1,1,1,16.79,0,0,5,A,17.204447,78.355087,534,44,140955,180221,11,1,15,5,4322,0,0,0,0,0,0,1.0,1.6CPTASF_6.60,0!")); diff --git a/src/test/java/org/traccar/protocol/SkypatrolProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SkypatrolProtocolDecoderTest.java index a248056f8..d2bf0f825 100644 --- a/src/test/java/org/traccar/protocol/SkypatrolProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SkypatrolProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class SkypatrolProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class SkypatrolProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new SkypatrolProtocolDecoder(null); + var decoder = inject(new SkypatrolProtocolDecoder(null)); verifyNull(decoder, binary( "000a02171101303131373232303031333537393833060200000006202020202020202020312020202020202030313137323230303133353739383320")); diff --git a/src/test/java/org/traccar/protocol/SmartSoleProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SmartSoleProtocolDecoderTest.java index 740d1b62c..9bad24865 100644 --- a/src/test/java/org/traccar/protocol/SmartSoleProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SmartSoleProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class SmartSoleProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class SmartSoleProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new SmartSoleProtocolDecoder(null); + var decoder = inject(new SmartSoleProtocolDecoder(null)); verifyPosition(decoder, text( "#GTXRP=359366080000385,8,180514200051,34.041981,-118.255806,60,1,1,7,1.80,180514200051,4.16,11")); diff --git a/src/test/java/org/traccar/protocol/SmokeyProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SmokeyProtocolDecoderTest.java index d806fd790..a8a9223c8 100644 --- a/src/test/java/org/traccar/protocol/SmokeyProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SmokeyProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class SmokeyProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class SmokeyProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new SmokeyProtocolDecoder(null); + var decoder = inject(new SmokeyProtocolDecoder(null)); verifyAttributes(decoder, binary( "534d0300865101019383025f0403000000000b86250200000c0000028f000102f8cc0900127f08")); diff --git a/src/test/java/org/traccar/protocol/SolarPoweredProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SolarPoweredProtocolDecoderTest.java index 7202aaefb..a18c7ee9b 100644 --- a/src/test/java/org/traccar/protocol/SolarPoweredProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SolarPoweredProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,7 @@ public class SolarPoweredProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new SolarPoweredProtocolDecoder(null); + var decoder = inject(new SolarPoweredProtocolDecoder(null)); verifyAttribute(decoder, binary( "7e850256553309440011003e81131914030600332301a61ed709209ff40014b89082020f0283100000f908000000440000003d1f19021784114161726f6e34475630312d323030333031057e"), diff --git a/src/test/java/org/traccar/protocol/SpotProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SpotProtocolDecoderTest.java index 7f8e2b58f..70b645b8f 100644 --- a/src/test/java/org/traccar/protocol/SpotProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SpotProtocolDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; import io.netty.handler.codec.http.HttpMethod; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class SpotProtocolDecoderTest extends ProtocolTest { @@ -9,7 +9,7 @@ public class SpotProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new SpotProtocolDecoder(null); + var decoder = inject(new SpotProtocolDecoder(null)); verifyPositions(decoder, request(HttpMethod.POST, "/", buffer( "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n", diff --git a/src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java index 1710ccbb9..f06bb9aac 100644 --- a/src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,7 @@ public class StarLinkProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new StarLinkProtocolDecoder(null); + var decoder = inject(new StarLinkProtocolDecoder(null)); decoder.setFormat("#IMEI#,#EDT#,#PDT#,#LAT#,#LONG#,#SPD#,#IGN#,#ODO#,#DUR#,#TDUR#,#LAC#,#CID#,#VIN#,#VBAT#,#EID#,#EDSC#,#DRV#,#SATU#,#CSS#,#OUT1#,#OUT2#,#CFL#"); diff --git a/src/test/java/org/traccar/protocol/StarcomProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/StarcomProtocolDecoderTest.java index 851d4eac6..6e2489bfe 100644 --- a/src/test/java/org/traccar/protocol/StarcomProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/StarcomProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class StarcomProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class StarcomProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new StarcomProtocolDecoder(null); + var decoder = inject(new StarcomProtocolDecoder(null)); verifyPosition(decoder, text( "|unit=416307,unittype=5,address=186.167.243.28,kind=14,software_version=14.02.18,hardware_type=17,gps_type=6,longitude=-67.85891,latitude=10.21988,datetime_actual=2019/05/07 21:59:38,network=TCPIP.1|\r\n")); diff --git a/src/test/java/org/traccar/protocol/StartekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/StartekProtocolDecoderTest.java index 6c2d39940..9b1362f5d 100644 --- a/src/test/java/org/traccar/protocol/StartekProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/StartekProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,10 +9,19 @@ public class StartekProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new StartekProtocolDecoder(null); + var decoder = inject(new StartekProtocolDecoder(null)); - verifyPosition(decoder, text( - "&&R187,860294046453690,000,0,,220105160656,A,22.994986,72.499711,15,0.9,2,222,55,121135784,404|98|147B|0000376A,24,0000001F,02,00,052E|01A3|0000|0000,1,010000|020000,,853|6|10|105|73|41|125|34|52")); + verifyAttribute(decoder, text( + "&&s148,868703050178631,000,37,,230704040211,A,22.678565,114.046011,31,0.5,0,339,77,8,460|0|249F|0AC2620D,27,0000001D,02,00,04F2|01A1|0000|0000,129,,,,949037"), + Position.KEY_HOURS, 9490000L); + + verifyAttribute(decoder, text( + "&&x164,869926040743375,000,0,,220705205955,A,33.326001,44.445318,10,1.2,0,57,8,925,418|40|038C|000083CD,31,00000015,00,00,0016|016A|0000|0000,1,,,686|33||44|99|14|124|11|8D"), + Position.KEY_FUEL_CONSUMPTION, 1.1); + + verifyAttribute(decoder, text( + "&&R187,860294046453690,000,0,,220105160656,A,22.994986,72.499711,15,0.9,2,222,55,121135784,404|98|147B|0000376A,24,0000001F,02,00,052E|01A3|0000|0000,1,010000|020000,,853|6|10|105|73|41|125|34|52"), + Position.KEY_FUEL_LEVEL, null); verifyPosition(decoder, text( "&&o142,860262050066062,000,27,,211111070826,V,28.653435,-106.077455,0,0.0,0,151,1412,918,0|0|4708|01402D19,6,0000001A,02,00,04C0|016C|0000|0000,1,,,BB")); diff --git a/src/test/java/org/traccar/protocol/StartekProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/StartekProtocolEncoderTest.java index 7eeb19edf..c87e421f2 100644 --- a/src/test/java/org/traccar/protocol/StartekProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/StartekProtocolEncoderTest.java @@ -1,17 +1,17 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class StartekProtocolEncoderTest extends ProtocolTest { @Test - public void testEncodeEngineStop() { + public void testEncodeEngineStop() throws Exception { - var encoder = new StartekProtocolEncoder(null); + var encoder = inject(new StartekProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/StbProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/StbProtocolDecoderTest.java index 74b30f9eb..dbdeee4d7 100644 --- a/src/test/java/org/traccar/protocol/StbProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/StbProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class StbProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class StbProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new StbProtocolDecoder(null); + var decoder = inject(new StbProtocolDecoder(null)); verifyAttributes(decoder, text( "{\"attrList\":[{\"id\":\"02101001\",\"value\":\"510101051161205774\"},{\"id\":\"02105001\",\"value\":\"-61\"},{\"id\":\"02102001\",\"value\":\"1\"},{\"doorId\":\"1\",\"id\":\"02104001\",\"value\":\"0\"},{\"doorId\":\"1\",\"id\":\"02106001\",\"value\":\"\"},{\"doorId\":\"1\",\"id\":\"02103001\",\"value\":\"0\"},{\"doorId\":\"1\",\"id\":\"02118001\",\"value\":\"1\"},{\"doorId\":\"2\",\"id\":\"02104001\",\"value\":\"0\"},{\"doorId\":\"2\",\"id\":\"02106001\",\"value\":\"\"},{\"doorId\":\"2\",\"id\":\"02103001\",\"value\":\"0\"},{\"doorId\":\"2\",\"id\":\"02118001\",\"value\":\"1\"},{\"doorId\":\"3\",\"id\":\"02104001\",\"value\":\"0\"},{\"doorId\":\"3\",\"id\":\"02106001\",\"value\":\"\"},{\"doorId\":\"3\",\"id\":\"02103001\",\"value\":\"0\"},{\"doorId\":\"3\",\"id\":\"02118001\",\"value\":\"1\"},{\"doorId\":\"4\",\"id\":\"02104001\",\"value\":\"0\"},{\"doorId\":\"4\",\"id\":\"02106001\",\"value\":\"\"},{\"doorId\":\"4\",\"id\":\"02103001\",\"value\":\"0\"},{\"doorId\":\"4\",\"id\":\"02118001\",\"value\":\"1\"},{\"doorId\":\"5\",\"id\":\"02104001\",\"value\":\"0\"},{\"doorId\":\"5\",\"id\":\"02106001\",\"value\":\"\"},{\"doorId\":\"5\",\"id\":\"02103001\",\"value\":\"1\"},{\"doorId\":\"5\",\"id\":\"02118001\",\"value\":\"1\"},{\"doorId\":\"6\",\"id\":\"02104001\",\"value\":\"2\"},{\"doorId\":\"6\",\"id\":\"02106001\",\"value\":\"BT106002320JPZZ210718002\"},{\"doorId\":\"6\",\"id\":\"02109001\",\"value\":\"98\"},{\"doorId\":\"6\",\"id\":\"02110001\",\"value\":\"100\"},{\"doorId\":\"6\",\"id\":\"01118001\",\"value\":\"27\"},{\"doorId\":\"6\",\"id\":\"01119001\",\"value\":\"26\"},{\"doorId\":\"6\",\"id\":\"01120001\",\"value\":\"28\"},{\"doorId\":\"6\",\"id\":\"02114001\",\"value\":\"0\"},{\"doorId\":\"6\",\"id\":\"02116001\",\"value\":\"0\"},{\"doorId\":\"6\",\"id\":\"02117001\",\"value\":\"0\"},{\"doorId\":\"6\",\"id\":\"01121001\",\"value\":\"2\"},{\"doorId\":\"6\",\"id\":\"02130001\",\"value\":\"0\"},{\"doorId\":\"6\",\"id\":\"01122001\",\"value\":\"4\"},{\"doorId\":\"6\",\"id\":\"02001001\",\"value\":\"000\"},{\"doorId\":\"6\",\"id\":\"02002001\",\"value\":\"000\"},{\"doorId\":\"6\",\"id\":\"01116001\",\"value\":\"20\"},{\"doorId\":\"6\",\"id\":\"01117001\",\"value\":\"3323\"},{\"doorId\":\"6\",\"id\":\"01117002\",\"value\":\"3324\"},{\"doorId\":\"6\",\"id\":\"01117003\",\"value\":\"3323\"},{\"doorId\":\"6\",\"id\":\"01117004\",\"value\":\"3324\"},{\"doorId\":\"6\",\"id\":\"01117005\",\"value\":\"3323\"},{\"doorId\":\"6\",\"id\":\"01117006\",\"value\":\"3324\"},{\"doorId\":\"6\",\"id\":\"01117007\",\"value\":\"3325\"},{\"doorId\":\"6\",\"id\":\"01117008\",\"value\":\"3324\"},{\"doorId\":\"6\",\"id\":\"01117009\",\"value\":\"3325\"},{\"doorId\":\"6\",\"id\":\"01117010\",\"value\":\"3326\"},{\"doorId\":\"6\",\"id\":\"01117011\",\"value\":\"3326\"},{\"doorId\":\"6\",\"id\":\"01117012\",\"value\":\"3324\"},{\"doorId\":\"6\",\"id\":\"01117013\",\"value\":\"3324\"},{\"doorId\":\"6\",\"id\":\"01117014\",\"value\":\"3323\"},{\"doorId\":\"6\",\"id\":\"01117015\",\"value\":\"3324\"},{\"doorId\":\"6\",\"id\":\"01117016\",\"value\":\"3324\"},{\"doorId\":\"6\",\"id\":\"01117017\",\"value\":\"3323\"},{\"doorId\":\"6\",\"id\":\"01117018\",\"value\":\"3323\"},{\"doorId\":\"6\",\"id\":\"01117019\",\"value\":\"3324\"},{\"doorId\":\"6\",\"id\":\"01117020\",\"value\":\"3323\"},{\"batteryId\":\"BT106002320JPZZ210718002\",\"doorId\":\"6\",\"id\":\"02103001\",\"value\":\"1\"},{\"batteryId\":\"BT106002320JPZZ210718002\",\"doorId\":\"6\",\"id\":\"02118001\",\"value\":\"1\"},{\"doorId\":\"7\",\"id\":\"02104001\",\"value\":\"0\"},{\"doorId\":\"7\",\"id\":\"02106001\",\"value\":\"\"},{\"doorId\":\"7\",\"id\":\"02103001\",\"value\":\"1\"},{\"doorId\":\"7\",\"id\":\"02118001\",\"value\":\"1\"},{\"doorId\":\"8\",\"id\":\"02104001\",\"value\":\"0\"},{\"doorId\":\"8\",\"id\":\"02106001\",\"value\":\"\"},{\"doorId\":\"8\",\"id\":\"02103001\",\"value\":\"0\"},{\"doorId\":\"8\",\"id\":\"02118001\",\"value\":\"1\"},{\"id\":\"02111001\",\"value\":\"0.0\"},{\"id\":\"02112001\",\"value\":\"0.0\"},{\"id\":\"02107001\",\"value\":\"229.1\"},{\"id\":\"02108001\",\"value\":\"1.005\"},{\"id\":\"02120001\",\"value\":\"143.76\"},{\"id\":\"02113001\",\"value\":\"29\"},{\"id\":\"02119001\",\"value\":\"1\"}],\"devId\":\"CHZD08KPD0210425046\",\"isFull\":1,\"msgType\":310,\"txnNo\":\"1636707944778\"}")); diff --git a/src/test/java/org/traccar/protocol/Stl060ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Stl060ProtocolDecoderTest.java index e13f74604..353fe4317 100644 --- a/src/test/java/org/traccar/protocol/Stl060ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Stl060ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Stl060ProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class Stl060ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Stl060ProtocolDecoder(null); + var decoder = inject(new Stl060ProtocolDecoder(null)); verifyPosition(decoder, text( "$1,357804048043099,D001,AP29AW0963,23/02/14,14:06:54,17248488N,078342226E,0.08,193.12,1,1,1,1,1,A"), diff --git a/src/test/java/org/traccar/protocol/SuntechFrameDecoderTest.java b/src/test/java/org/traccar/protocol/SuntechFrameDecoderTest.java index 99cbeac2f..0db6e756c 100644 --- a/src/test/java/org/traccar/protocol/SuntechFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SuntechFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class SuntechFrameDecoderTest extends ProtocolTest { @@ -8,7 +8,11 @@ public class SuntechFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new SuntechFrameDecoder(); + var decoder = inject(new SuntechFrameDecoder()); + + verifyFrame( + binary("53543330305545583b3531313639393339383b34353b3331353b32303232303632333b31383a32343a35383b3661313332393b2d32392e3735343934373b2d3035372e3038353838353b3030302e3030303b3030302e30303b31303b313b37323b302e30303b3030303030303b31323b0201100110011090011001100110011001100110fe3b39303b3030303030303b332e393b313b30303030303030303030303030303b30"), + decoder.decode(null, null, binary("53543330305545583b3531313639393339383b34353b3331353b32303232303632333b31383a32343a35383b3661313332393b2d32392e3735343934373b2d3035372e3038353838353b3030302e3030303b3030302e30303b31303b313b37323b302e30303b3030303030303b31323b0201100110011090011001100110011001100110fe3b39303b3030303030303b332e393b313b30303030303030303030303030303b300d"))); verifyFrame( binary("81004e05200013383fffff3401000301130a0512080400000000000000000000000047f9d5846a06810072225214010100020300a8002604c1000004b000000470000025a100000000000025c4000000a6"), diff --git a/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java index 098758728..cbb68132f 100644 --- a/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java @@ -1,87 +1,30 @@ package org.traccar.protocol; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; public class SuntechProtocolDecoderTest extends ProtocolTest { @Test - public void testDecodeTemperature() throws Exception { - - var decoder = new SuntechProtocolDecoder(null); - - decoder.setHbm(true); - decoder.setIncludeAdc(true); - - verifyAttribute(decoder, buffer( - "ST600STT;008594432;20;492;20200212;18:58:30;060bb0e1;334;20;36bb;45;+19.337897;-099.064489;000.398;000.00;12;1;5049883;13.61;100100;2;1198;013762;4.2;1;4.68"), - Position.PREFIX_ADC + 1, 4.68); - - decoder.setIncludeTemp(true); + public void testDecode() throws Exception { - verifyAttribute(decoder, buffer( - "ST600STT;008350848;35;523;20191102;13:49:46;0bf14fdb;334;20;2f19;57;+20.466737;-100.825455;000.006;000.00;11;1;10274175;11.36;00000000;1;0300;018353;4.2;1;0.00;;;;00000000000000;0;28EE56B911160234:+13.7;:;:"), - Position.PREFIX_TEMP + 2, 13.7); + var decoder = inject(new SuntechProtocolDecoder(null)); - verifyPosition(decoder, buffer( - "ST300STT;205173382;07;564;20160322;23:23:18;232e19;+19.288278;-099.128750;000.122;000.00;9;1;478391;11.53;000100;1;9498;079324;4.3;1;0.00;0.00;0.00;00000000000000;0;2898E16006000058:-20.8;2861626006000039:+2.5;:")); + verifyAttributes(decoder, buffer( + "ST410STT;109815653;445;03;16531;724;10;-77;6011;7;16273;724;10;7511;0;0;13903;724;10;6011;0;0;7671;724;10;7111;0;0;16533;724;10;6011;0;0;0;0;0;0;0;0;0;0;0;0;0;0;3.86;0;0098;1;003;;;;;;;;;")); verifyPosition(decoder, buffer( - "ST300EVT;205173382;07;564;20160322;23:23:18;232e19;+19.288278;-099.128750;000.122;000.00;9;1;478391;11.53;000100;2;1;9498;079324;4.3;1;0.00;0.00;0.00;00000000000000;0;2898E16006000058:-20.8;2861626006000039:+2.5;:")); - - verifyPosition(decoder, buffer( - "ST600STT;008349958;35;523;20181112;00:49:30;0bf10d4e;334;20;2f19;22;+20.552718;-100.824478;050.021;095.41;12;1;1303603;14.30;10000000;4;8911;001987;4.1;0;0.00;;;;00000000000000;0;2826C8A70800000C:+26.6;:;:")); - - } - - @Test - public void testDecodeRpm() throws Exception { - - var decoder = new SuntechProtocolDecoder(null); - - decoder.setHbm(true); - decoder.setIncludeRpm(true); + "ALT;0840037569;FFFFFF;84;1.0.6;0;20221228;11:33:05;00004490;724;11;05D3;33;-22.845935;-46.322000;0.00;0.00;18;0;00000001;00000000;99;;;;08E3800F;4.1;12.37;0;0;0;0;4;;;;")); verifyAttribute(decoder, buffer( - "ST300STT;907131077;04;706;20190227;23:59:34;cc719;-12.963490;-038.499587;000.067;000.00;7;1;57095;12.50;000000;1;0337;000207;0.0;1;0;012E717F010000;1"), - Position.KEY_RPM, 0); - - } - - @Test - public void testDecodeHours() throws Exception { - - var decoder = new SuntechProtocolDecoder(null); - - decoder.setHbm(true); - - verifyAttribute(decoder, buffer( - "ST300ALT;007239104;40;313;20190112;01:07:16;c99139;+04.703287;-074.148897;000.000;189.72;21;1;425512;12.61;100000;33;003188;4.1;1"), - Position.KEY_HOURS, 3188 * 60000L); - - } - - @Test - public void testDecodeDriver() throws Exception { - - var decoder = new SuntechProtocolDecoder(null); + "ALT;0950030205;3FFFFF;95;1.0.11;0;20221017;21:41:30;02F2F402;334;20;0F1D;45;+25.791061;-100.170745;0.00;0.00;18;1;00000101;00000000;42;2;"), + Position.KEY_ALARM, Position.ALARM_SOS); verifyAttribute(decoder, buffer( - "ST300HTE;511050566;45;308;20200909;13:38:38;0;12.50;001354;0.0;1;0;1;1;0;-27.636632;-052.277933;-27.636675;-052.277947;000.000;002.296;0;00000000000000"), - Position.KEY_DRIVER_UNIQUE_ID, "00000000000000"); - - verifyAttribute(decoder, buffer( - "ST300HTE;100850001;04;248;20110101;00:13:52;167559;12.28;004005;0.0;1;0;3;3;0;-22.881018;-047.070831;-22.881018;-047.070831;000.000;000.000;0;0;3;0;0;0;01E04D44160000"), - Position.KEY_DRIVER_UNIQUE_ID, "01E04D44160000"); - - } - - @Test - public void testDecode() throws Exception { - - var decoder = new SuntechProtocolDecoder(null); + "RES;4309999001;04;02;TEST"), + Position.KEY_RESULT, "04;02;TEST"); verifyPosition(decoder, buffer( "BLE;1140000053;114;1.0.1;20211001;17:27:09;+28.433465;-82.565891;1;-43;-46;-41;ACB89523EF68;247;0;0")); @@ -92,6 +35,17 @@ public class SuntechProtocolDecoderTest extends ProtocolTest { verifyNull(decoder, buffer( "BSA;0820012345;001FFF;82;1.0.0;1;20191203;17:00:51;+32.691615;-117.297160;1;-55;68:11:6A:FD:1A:A7;6AA5;1DE8")); + verifyAttributes(decoder, binary( + "53543330305545583b3531313639393339383b34353b3331353b32303232303632333b31383a32343a35383b3661313332393b2d32392e3735343934373b2d3035372e3038353838353b3030302e3030303b3030302e30303b31303b313b37323b302e30303b3030303030303b31323b0201100110011090011001100110011001100110fe3b39303b3030303030303b332e393b313b30303030303030303030303030303b30")); + + verifyAttribute(decoder, buffer( + "ST300UEX;511248287;45;311;20220701;18:42:08;14c943;-22.975257;-043.373065;000.000;000.00;0;0;0;12.14;100010;19;RFID:008FB2BEBA39\r\n;3E;000135;4.1;1;00000000000000;0"), + "serial", "RFID:008FB2BEBA39"); + + verifyAttribute(decoder, buffer( + "ST300UEX;100850000;01;010;20081017;07:41:56;2F100;+37.478519;+126.886819;000.012;000.00;9;1;0;15.30;001100;25;Welcome to Suntech World!;12;0;4.5;1"), + "serial", "Welcome to Suntech World!"); + verifyAttribute(decoder, buffer( "ST300UEX;511331307;45;311;20210420;12:41:01;12361;-01.280825;-047.931773;000.000;000.00;16;1;0;12.54;000000;23;GTSL|6|1|0|9255143|2|;6F;000276;0.0;1;00000000000000;0"), Position.KEY_DRIVER_UNIQUE_ID, "9255143"); @@ -258,11 +212,11 @@ public class SuntechProtocolDecoderTest extends ProtocolTest { } - @Ignore + @Disabled @Test public void testDecodeCrash() throws Exception { - var decoder = new SuntechProtocolDecoder(null); + var decoder = inject(new SuntechProtocolDecoder(null)); verifyAttribute(decoder, binary( "4352523b303931303030303036333b313b313b303135303b16011c150f0ad82f6c0000000000ae037085fbff7700fd00faff6300f30000006800fb000d007100fa00f32f6c00000000005e044a80fcff6f000301e1ff5d00e900e1ff6400e600f4ff5b00ec000a306c00000000002104248306006c000501fcff5b00e00001006e000101eeff4e00e10022306c00000000005c041a7e00006a00010100005d00f800b5ff7cffdf0050009300fc003b44350d"), @@ -270,4 +224,75 @@ public class SuntechProtocolDecoderTest extends ProtocolTest { } + @Test + public void testDecodeTemperature() throws Exception { + + var decoder = inject(new SuntechProtocolDecoder(null)); + + decoder.setHbm(true); + decoder.setIncludeAdc(true); + + verifyAttribute(decoder, buffer( + "ST600STT;008594432;20;492;20200212;18:58:30;060bb0e1;334;20;36bb;45;+19.337897;-099.064489;000.398;000.00;12;1;5049883;13.61;100100;2;1198;013762;4.2;1;4.68"), + Position.PREFIX_ADC + 1, 4.68); + + decoder.setIncludeTemp(true); + + verifyAttribute(decoder, buffer( + "ST600STT;008350848;35;523;20191102;13:49:46;0bf14fdb;334;20;2f19;57;+20.466737;-100.825455;000.006;000.00;11;1;10274175;11.36;00000000;1;0300;018353;4.2;1;0.00;;;;00000000000000;0;28EE56B911160234:+13.7;:;:"), + Position.PREFIX_TEMP + 2, 13.7); + + verifyPosition(decoder, buffer( + "ST300STT;205173382;07;564;20160322;23:23:18;232e19;+19.288278;-099.128750;000.122;000.00;9;1;478391;11.53;000100;1;9498;079324;4.3;1;0.00;0.00;0.00;00000000000000;0;2898E16006000058:-20.8;2861626006000039:+2.5;:")); + + verifyPosition(decoder, buffer( + "ST300EVT;205173382;07;564;20160322;23:23:18;232e19;+19.288278;-099.128750;000.122;000.00;9;1;478391;11.53;000100;2;1;9498;079324;4.3;1;0.00;0.00;0.00;00000000000000;0;2898E16006000058:-20.8;2861626006000039:+2.5;:")); + + verifyPosition(decoder, buffer( + "ST600STT;008349958;35;523;20181112;00:49:30;0bf10d4e;334;20;2f19;22;+20.552718;-100.824478;050.021;095.41;12;1;1303603;14.30;10000000;4;8911;001987;4.1;0;0.00;;;;00000000000000;0;2826C8A70800000C:+26.6;:;:")); + + } + + @Test + public void testDecodeRpm() throws Exception { + + var decoder = inject(new SuntechProtocolDecoder(null)); + + decoder.setHbm(true); + decoder.setIncludeRpm(true); + + verifyAttribute(decoder, buffer( + "ST300STT;907131077;04;706;20190227;23:59:34;cc719;-12.963490;-038.499587;000.067;000.00;7;1;57095;12.50;000000;1;0337;000207;0.0;1;0;012E717F010000;1"), + Position.KEY_RPM, 0); + + } + + @Test + public void testDecodeHours() throws Exception { + + var decoder = inject(new SuntechProtocolDecoder(null)); + + decoder.setHbm(true); + + verifyAttribute(decoder, buffer( + "ST300ALT;007239104;40;313;20190112;01:07:16;c99139;+04.703287;-074.148897;000.000;189.72;21;1;425512;12.61;100000;33;003188;4.1;1"), + Position.KEY_HOURS, 3188 * 60000L); + + } + + @Test + public void testDecodeDriver() throws Exception { + + var decoder = inject(new SuntechProtocolDecoder(null)); + + verifyAttribute(decoder, buffer( + "ST300HTE;511050566;45;308;20200909;13:38:38;0;12.50;001354;0.0;1;0;1;1;0;-27.636632;-052.277933;-27.636675;-052.277947;000.000;002.296;0;00000000000000"), + Position.KEY_DRIVER_UNIQUE_ID, "00000000000000"); + + verifyAttribute(decoder, buffer( + "ST300HTE;100850001;04;248;20110101;00:13:52;167559;12.28;004005;0.0;1;0;3;3;0;-22.881018;-047.070831;-22.881018;-047.070831;000.000;000.000;0;0;3;0;0;0;01E04D44160000"), + Position.KEY_DRIVER_UNIQUE_ID, "01E04D44160000"); + + } + } diff --git a/src/test/java/org/traccar/protocol/SupermateProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SupermateProtocolDecoderTest.java index 29ec670aa..4fa209fb7 100755 --- a/src/test/java/org/traccar/protocol/SupermateProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SupermateProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class SupermateProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class SupermateProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new SupermateProtocolDecoder(null); + var decoder = inject(new SupermateProtocolDecoder(null)); verifyPosition(decoder, text( "2:359672050130411:1:*,00000000,XT,A,10031b,140b28,80ad4c72,81ba2d2c,06ab,238c,020204010000,12,0,0000,0003e6")); diff --git a/src/test/java/org/traccar/protocol/SviasProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SviasProtocolDecoderTest.java index 8a3a74753..694a178f4 100644 --- a/src/test/java/org/traccar/protocol/SviasProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SviasProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class SviasProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class SviasProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new SviasProtocolDecoder(null); + var decoder = inject(new SviasProtocolDecoder(null)); verifyPosition(decoder, text( "[7061,3041,57,20324277,710,40618,141342,-93155840,-371754060,0,20469,0,16,1,0,0,11323,100,9,,32,4695")); diff --git a/src/test/java/org/traccar/protocol/SwiftechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SwiftechProtocolDecoderTest.java index 10b033985..6b473b38c 100644 --- a/src/test/java/org/traccar/protocol/SwiftechProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SwiftechProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class SwiftechProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class SwiftechProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new SwiftechProtocolDecoder(null); + var decoder = inject(new SwiftechProtocolDecoder(null)); verifyPosition(decoder, text( "@@861551041946971,,0,102040,1023.9670,N,07606.8160,E,2.26,151220,A,0127,1,1,03962,00000,#")); diff --git a/src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java index ba981598d..7b9841d68 100644 --- a/src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,13 @@ public class T55ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new T55ProtocolDecoder(null); + var decoder = inject(new T55ProtocolDecoder(null)); + + verifyAttributes(decoder, text( + "$GPTXT,NET,1003,A1,-53,232 01*77")); + + verifyPosition(decoder, text( + "$PUBX,00,130209.00,3650.51159,N,01346.10602,E,785.947,D3,4.1,5.2,0.163,87.43,-0.054,7.0,0.88,1.21,0.88,24,01012,0*6D")); verifyPosition(decoder, text( "QZE,868994033976700,35,28062020,113553,22.13673,114.57263,0,22,A,0")); @@ -121,7 +127,7 @@ public class T55ProtocolDecoderTest extends ProtocolTest { // Maxon devices can send NMEA before identification - var decoder = new T55ProtocolDecoder(null); + var decoder = inject(new T55ProtocolDecoder(null)); verifyNull(decoder, text( "$GPRMC,012006,A,4828.10,N,1353.52,E,0.00,0.00,180915,020.3,E*42")); diff --git a/src/test/java/org/traccar/protocol/T57FrameDecoderTest.java b/src/test/java/org/traccar/protocol/T57FrameDecoderTest.java index dd5929b04..1e39298fa 100644 --- a/src/test/java/org/traccar/protocol/T57FrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/T57FrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class T57FrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class T57FrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new T57FrameDecoder(); + var decoder = inject(new T57FrameDecoder()); verifyFrame( binary("2a5435372346312354353731313137303031233330313131372330303038343323323233342e31333033234e2330383832362e313731342345232b302e3234322c2b302e3130392c2d302e37383923302e30303023362e323030303023413223342e3223"), diff --git a/src/test/java/org/traccar/protocol/T57ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/T57ProtocolDecoderTest.java index 376aab1da..e0fd2e800 100644 --- a/src/test/java/org/traccar/protocol/T57ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/T57ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class T57ProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class T57ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new T57ProtocolDecoder(null); + var decoder = inject(new T57ProtocolDecoder(null)); verifyPosition(decoder, text( "*T57#F1#T571117001#301117#000843#2234.1303#N#08826.1714#E#+0.242,+0.109,-0.789#0.000#6.20000#A2#4.2#")); diff --git a/src/test/java/org/traccar/protocol/T622IridiumProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/T622IridiumProtocolDecoderTest.java new file mode 100644 index 000000000..ff5d5b0a0 --- /dev/null +++ b/src/test/java/org/traccar/protocol/T622IridiumProtocolDecoderTest.java @@ -0,0 +1,21 @@ +package org.traccar.protocol; + +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; + +public class T622IridiumProtocolDecoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + var decoder = inject(new T622IridiumProtocolDecoder(null)); + + decoder.setFormat("01,02,03,04,05,08"); + + verifyPosition(decoder, binary( + "01003501001c68b2cb1733303034333430363735343836353000016e000064b5f497020013234c5ea0ff1c365d0600b1482c010000cf0004"), + position("2023-07-18 02:10:08.000", true, -6.26732, 106.77200)); + + } + +} diff --git a/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java index 1dd4e8619..3f62a834c 100644 --- a/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,13 @@ public class T800xProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new T800xProtocolDecoder(null); + var decoder = inject(new T800xProtocolDecoder(null)); + + verifyAttributes(decoder, binary( + "272704004901380864112055585747c612230321220006000036435fc8acc2ee600f420000000000000000909019003900001356a18000012c0000a8c00000001e20d4800000c00000")); + + verifyAttributes(decoder, binary( + "2525110055000208677300508924902206262035310c540045004c00430045004c0004454447450847534d20313930300f323134303734323036373835323839143839333430373131373930303936383037363846")); verifyAttributes(decoder, binary( "27271000247bd00860112047066487210407034238000005d7d17365e625ff640a730148")); diff --git a/src/test/java/org/traccar/protocol/T800xProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/T800xProtocolEncoderTest.java index 1d17ee5d5..4741f5c92 100644 --- a/src/test/java/org/traccar/protocol/T800xProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/T800xProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; @@ -9,7 +9,7 @@ public class T800xProtocolEncoderTest extends ProtocolTest { @Test public void testEncode() throws Exception { - var encoder = new T800xProtocolEncoder(null); + var encoder = inject(new T800xProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/TaipProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TaipProtocolDecoderTest.java index 56af8830a..bc5901fb0 100644 --- a/src/test/java/org/traccar/protocol/TaipProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TaipProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,7 @@ public class TaipProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new TaipProtocolDecoder(null); + var decoder = inject(new TaipProtocolDecoder(null)); verifyAttribute(decoder, text( ">RUS00,111220124402-3138067-06417623000012200FF,000000000000000000000000000,0000000111,15640422,00000,+25.5,00000,51;ID=CST3G0443;#IP1:089F;*34<"), diff --git a/src/test/java/org/traccar/protocol/TechTltProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TechTltProtocolDecoderTest.java index c5de8e62a..28a917096 100644 --- a/src/test/java/org/traccar/protocol/TechTltProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TechTltProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TechTltProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class TechTltProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new TechTltProtocolDecoder(null); + var decoder = inject(new TechTltProtocolDecoder(null)); verifyPosition(decoder, text( "002422269*POS=Y,16:21:20,25/11/09,3809.8063N,01444.7438E,4.17,117.23,0.4,09,40076,56341\r\n"), diff --git a/src/test/java/org/traccar/protocol/TechtoCruzFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TechtoCruzFrameDecoderTest.java index dc4afb784..1e1f8d431 100644 --- a/src/test/java/org/traccar/protocol/TechtoCruzFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TechtoCruzFrameDecoderTest.java @@ -1,16 +1,16 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class TechtoCruzFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new TechtoCruzFrameDecoder(); + var decoder = inject(new TechtoCruzFrameDecoder()); assertEquals( buffer("$$A35,RESPO|G33|8612345678910|CRUZ,*E3"), diff --git a/src/test/java/org/traccar/protocol/TechtoCruzProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TechtoCruzProtocolDecoderTest.java index 4cef682b4..b512f9968 100644 --- a/src/test/java/org/traccar/protocol/TechtoCruzProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TechtoCruzProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TechtoCruzProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class TechtoCruzProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new TechtoCruzProtocolDecoder(null); + var decoder = inject(new TechtoCruzProtocolDecoder(null)); verifyPosition(decoder, text( "$$A120,8612345678910,211005105836,A,FLEX,KCB 947C,000.0,0,-1.38047,S,36.93951,E,1648.4,243.140,21,28,12.1,3.7,0,1,0,0,0,*F6")); diff --git a/src/test/java/org/traccar/protocol/TekFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TekFrameDecoderTest.java index f315e6432..fa6a490a4 100644 --- a/src/test/java/org/traccar/protocol/TekFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TekFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TekFrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class TekFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new TekFrameDecoder(); + var decoder = inject(new TekFrameDecoder()); verifyFrame( binary("020315048715E70861074028023219026200400A0340002C007F0009000000000000000000402842064028420641284206402844064128440640284406402844064028440641284406402844060010010C04052B000253000000000001060A0000000000000228330000FF0000FF360014B394"), diff --git a/src/test/java/org/traccar/protocol/TekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TekProtocolDecoderTest.java index a371ffe6b..9976b9bac 100644 --- a/src/test/java/org/traccar/protocol/TekProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TekProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TekProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class TekProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new TekProtocolDecoder(null); + var decoder = inject(new TekProtocolDecoder(null)); verifyPosition(decoder, binary( "0501E304E00E76086107502100455111492C33332C3137303935342E302C353235352E393933344E2C30303833322E34333935572C322E312C3133342E382C322C302E30302C302E302C302E302C3234303931352C30362C3C45")); diff --git a/src/test/java/org/traccar/protocol/TelemaxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TelemaxProtocolDecoderTest.java index 8c3650f4e..8770e9451 100644 --- a/src/test/java/org/traccar/protocol/TelemaxProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TelemaxProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TelemaxProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class TelemaxProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new TelemaxProtocolDecoder(null); + var decoder = inject(new TelemaxProtocolDecoder(null)); verifyNull(decoder, text( "%067374070128")); diff --git a/src/test/java/org/traccar/protocol/TelicFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TelicFrameDecoderTest.java index 125b61d48..ba3808534 100644 --- a/src/test/java/org/traccar/protocol/TelicFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TelicFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TelicFrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class TelicFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new TelicFrameDecoder(); + var decoder = inject(new TelicFrameDecoder()); verifyFrame( binary("303032363230333339337c3232367c31307c303032303034303130"), diff --git a/src/test/java/org/traccar/protocol/TelicProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TelicProtocolDecoderTest.java index 03824d91b..44e63b8a6 100644 --- a/src/test/java/org/traccar/protocol/TelicProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TelicProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TelicProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class TelicProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new TelicProtocolDecoder(null); + var decoder = inject(new TelicProtocolDecoder(null)); verifyNull(decoder, text( "0026355565071347499|206|01|001002008")); diff --git a/src/test/java/org/traccar/protocol/TeltonikaFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TeltonikaFrameDecoderTest.java index 7787907b6..8d0cafcd9 100644 --- a/src/test/java/org/traccar/protocol/TeltonikaFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TeltonikaFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TeltonikaFrameDecoderTest extends ProtocolTest { @@ -8,10 +8,10 @@ public class TeltonikaFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new TeltonikaFrameDecoder(); + var decoder = inject(new TeltonikaFrameDecoder()); verifyFrame( - binary("000F313233343536373839303132333435"), + binary("ff"), decoder.decode(null, null, binary("FF000F313233343536373839303132333435"))); verifyFrame( diff --git a/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java index c3c32369c..0b4f1d243 100644 --- a/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java @@ -1,19 +1,36 @@ package org.traccar.protocol; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; +import org.traccar.model.Position; public class TeltonikaProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new TeltonikaProtocolDecoder(null, false); + var decoder = inject(new TeltonikaProtocolDecoder(null, false)); verifyNull(decoder, binary( "000F313233343536373839303132333435")); + verifyAttribute(decoder, binary( + "00000000000000240d01060000001c642b3ad14754534c7c367c317c307c31323734393838347c317c0d0a010000ec11"), + Position.KEY_DRIVER_UNIQUE_ID, "12749884"); + + verifyPositions(decoder, binary( + "00000000000000a28e0100000183ac617e3001123eb99b1e142db4000000000000000000001d000900f000005000001503004500011e1801212d01242a012722012a18001100b5000000b600000018000000cd151000431c2d011f6981012047d701226981012347d901256981012647d8012869810129e6f304b0000304b1000304b2000304b30003000100f10000639d0002000b0000000214bf12fe000e0000000029d18c95000001000051b6")); + + verifyPositions(decoder, binary( + "00000000000004258e0400000182a701b49301d5d90ab7ebe4aae101be003d12000000f7003d000e00f70100ef0000f00000500500150200c800004501000100001d00001400001600001700007157010701001d00b5000b00b60006004230400018000000cd223f00ce741700430f8900440000000d00010011ffe50012001f0013ffce000f03e800190bb8001a0bb8001b0bb8001c0bb800560bb800680bb8006a0bb8006c0bb8010e0000011100000114000001170000014f0000015000000151000001520000000a00f100011d2a00c700000000001000b9addc000c0000acb600040000000001320000000001330000000001340000000001350000000001c1000000000003000b000000d14675f36000ee0000000000000000000e0000000003fd509f0005014b0000014c0000014d0000014e0000018300222d3333373333382e0100000053a6fb624588040001ba86064f0eae51c0fdaf4d3de500000182a701b82001d5d90ab7ebe4aae101be003d12000000f0003c000d00ef0000f00100500500150200c800004501000100001d00001400001600001700007152010701001d00b5000900b60006004217e50018000000cd223f00ce741700430f5c00440000000d00010011fed60012fd1d0013f1f2000f03e800190bb8001a0bb8001b0bb8001c0bb800560bb800680bb8006a0bb8006c0bb8010e0000011100000114000001170000014f0000015000000151000001520000000a00f100011d2a00c700000000001000b9addc000c0000acb600040000000001320000000001330000000001340000000001350000000001c1000000000003000b000000d14675f36000ee0000000000000000000e0000000003fd509f0005014b0000014c0000014d0000014e0000018300222d3333373333352e353833332d303730373139362e323333332b3030302e3434362f00000182a701bc0801d5d90ab7ebe4aae101be003d12000000fc003d000e00ef0000f00100500500150200c800004501000100001d0000140000160000170000714d01070100fc01001d00b5000900b60006004217e50018000000cd223f00ce741700430f5c00440000000d00010011fed60012fd1d0013f1f2000f03e800190bb8001a0bb8001b0bb8001c0bb800560bb800680bb8006a0bb8006c0bb8010e0000011100000114000001170000014f0000015000000151000001520000000a00f100011d2a00c700000000001000b9addc000c0000acb600040000000001320000000001330000000001340000000001350000000001c1000000000003000b000000d14675f36000ee0000000000000000000e0000000003fd509f0005014b0000014c0000014d0000014e0000018300222d3333373333352e353833332d303730373139362e323333332b3030302e3434362f00000182a7018d8d01d5d8ffa6ebe4a0ca01be006111000000f70001000100f70500000000000000000400003a10")); + + verifyPositions(decoder, binary( + "00000000000003831004000001735ace37f80000e3b9331c71e290006900e211005100fd072e1600010100160300470300f00100150400b20000c80000ef01009000004f00005101005201005300005538006e00006f00007a03007d00007f5600890000fd0200fe1f09004326b00044000000b5000b00b6000600427029001800540046015d00ce4ec10080000f0f00f10000515400cd007404ab00d80f5022a1005000000054005400000000005600015568005700000060005800000420006800001113006d303330300071fffd8c85008700000020008800000002008a000155f5008b0000b86000000001735ace3ca80000e3b08a1c71dd29006900e311005100fd072e1600010100160300470300f00100150400b20000c80000ef01009000004f00005101005201005300005537006e00006f00007a03007d00007f5600890000fd0200fe1d09004326ac0044000000b5000b00b600060042701f001800540046015d00ce4ec10080000f0f00f10000515400cd007404ab00d80f5022ce00500000005400540000000000560001556800570000006000580000041f006800001113006d303330300071fffd8c85008700000020008800000002008a000155f5008b0000b86000000001735ace3fc80000e3a7c01c71d7c2006900e311005100fd072e1600010100160300470300f00100150400b20000c80000ef01009000004f00005101005201005300005537006e00006f00007a03007d00007f5600890000fd0200fe2309004326ac0044000000b5000b00b6000600427015001800540046015e00ce4ec10080000f0f00f10000515400cd007404ab00d80f5022e700500000005400540000000000560001556800570000006000580000041f006800001113006d303330300071fffd8c85008700000020008800000002008a000155f5008b0000b86000000001735ace3ffa0000e3a7c01c71d7c2006900e311005100fd072e1600010100160300470300f00100150400b20000c80000ef01009000004f00005101005201005300005537006e00006f00007a03007d00007f5600890000fd0300fe2309004326ac0044000000b5000b00b6000600427015001800540046015e00ce4ec10080000f0f00f10000515400cd007404ab00d80f5022e700500000005400540000000000560001556800570000006000580000041f006800001113006d303330300071fffd8c85008700000020008800000002008a000155f5008b0000b86000040000eb85")); + + verifyPositions(decoder, binary( + "00000000000000e708030000018293d62060000de1f6a62159767e00000000000000000b074504f00050041500c801ef004f630342320043000344000001f100006019000000018291b0c790000de1f6a62159767e006e0144070000ef12074503f00050051505c800ef004f0207b5000eb6000b423324180000ce00dc43001144000004c700000007f100006019cd0003c7776300ea4e2e000000018291aff0b8000de1f91f21597405006f00f61300080012074503f00150051504c800ef014f0207b50009b600054236c1180008ce00dc43003f44000004c700000003f100006019cd0003c7776300ea4e2700030000a48d")); + verifyPositions(decoder, binary( "00000000000000da08030000017fcedf499600280431be0eded45d0038012d100000fa100901000200b300b4004501500415034702fa00054232a1180000cd3b2fce281d43001f02c700000006f10000a029000000017fcedea99600280432070eded3dd00380046130009000f0801010200b300b400450150051502470205423276180009cd3b2fce281d43001f02c700000027f10000a0290000000179d50853180027f65d3f0ed67212001500f1110061000f0801010200b300b4004501500515034702054234f4180061cd53d1ce28c043003e02c700000147f1000000290003000052cb")); @@ -113,6 +130,9 @@ public class TeltonikaProtocolDecoderTest extends ProtocolTest { verifyPositions(decoder, binary( "000000000000004a08010000015ebc1da508002411926621f15246010b00b913005e000f06ef01f00150011505c800450108b5000bb6000642381b18005ecd0318ce19cd430f5844000001f1000061a900010000c8e1")); + verifyNull(decoder, binary( + "ff")); + decoder.setExtended(true); verifyPositions(decoder, false, binary( @@ -127,7 +147,7 @@ public class TeltonikaProtocolDecoderTest extends ProtocolTest { @Test public void testDecodePhoto() throws Exception { - var decoder = new TeltonikaProtocolDecoder(null, false); + var decoder = inject(new TeltonikaProtocolDecoder(null, false)); verifyNull(decoder, binary( "000F313233343536373839303132333435")); @@ -143,11 +163,11 @@ public class TeltonikaProtocolDecoderTest extends ProtocolTest { } - @Ignore + @Disabled @Test public void testDecodeConnectionless() throws Exception { - var decoder = new TeltonikaProtocolDecoder(null, true); + var decoder = inject(new TeltonikaProtocolDecoder(null, true)); verifyPositions(decoder, false, binary( "0049cafe0122000f33353734353430373237313339373508010000015d3766f6a800003eef961ec6215e0063006d09003100070401000200f001c8000242381c18003201c7000000e10001")); diff --git a/src/test/java/org/traccar/protocol/TeltonikaProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/TeltonikaProtocolEncoderTest.java index 008a2b9dd..04e8afe0b 100644 --- a/src/test/java/org/traccar/protocol/TeltonikaProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/TeltonikaProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; @@ -9,7 +9,7 @@ public class TeltonikaProtocolEncoderTest extends ProtocolTest { @Test public void testEncode() throws Exception { - var encoder = new TeltonikaProtocolEncoder(null); + var encoder = inject(new TeltonikaProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/TeraTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TeraTrackProtocolDecoderTest.java index 6f96f73ad..674db0ad5 100644 --- a/src/test/java/org/traccar/protocol/TeraTrackProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TeraTrackProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TeraTrackProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class TeraTrackProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new TeraTrackProtocolDecoder(null); + var decoder = inject(new TeraTrackProtocolDecoder(null)); verifyAttributes(decoder, text( "{\"MDeviceID\":\"022043756090\",\"DiviceType\":\"1\",\"DataType\":\"1\",\"DataLength\":\"69\",\"DateTime\":\"2022-03-09 10:56:01\",\"Latitude\":\"-6.846451\",\"Longitude\":\"39.316324\",\"LongitudeState\":\"1\",\"LatitudeState\":\"0\",\"Speed\":\"90\",\"Mileage\":\"0\",\"FenceAlarm\":\"0\",\"AreaAlarmID\":\"0\",\"LockCutOff\":\"0\",\"SealTampered\":\"0\",\"MessageAck\":\"1\",\"LockRope\":\"1\",\"LockStatus\":\"1\",\"LockOpen\":\"0\",\"PasswordError\":\"0\",\"CardNo\":\"60000644\",\"IllegalCard\":\"0\",\"LowPower\":\"0\",\"UnCoverBack\":\"0\",\"CoverStatus\":\"1\",\"LockStuck\":\"0\",\"Power\":\"79\",\"GSM\":\"16\",\"IMEI\":\"860922043756090\",\"Index\":\"20\",\"Slave\":[]}")); diff --git a/src/test/java/org/traccar/protocol/ThinkPowerProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ThinkPowerProtocolDecoderTest.java index ff808e667..0271bf3a2 100644 --- a/src/test/java/org/traccar/protocol/ThinkPowerProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ThinkPowerProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ThinkPowerProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class ThinkPowerProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new ThinkPowerProtocolDecoder(null); + var decoder = inject(new ThinkPowerProtocolDecoder(null)); verifyNull(decoder, binary( "0103002C01020F38363737333030353038323030343606544C3930344111522D312E302E31372E32303231303431300011C3")); diff --git a/src/test/java/org/traccar/protocol/ThinkRaceProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ThinkRaceProtocolDecoderTest.java index 39e7ed473..73e40ce55 100644 --- a/src/test/java/org/traccar/protocol/ThinkRaceProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ThinkRaceProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ThinkRaceProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class ThinkRaceProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new ThinkRaceProtocolDecoder(null); + var decoder = inject(new ThinkRaceProtocolDecoder(null)); verifyNull(decoder, binary( "48415349483031343730303134382C8000100134363030303134363139363239343806FF")); diff --git a/src/test/java/org/traccar/protocol/ThurayaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ThurayaProtocolDecoderTest.java new file mode 100644 index 000000000..b9bc1dbc2 --- /dev/null +++ b/src/test/java/org/traccar/protocol/ThurayaProtocolDecoderTest.java @@ -0,0 +1,24 @@ +package org.traccar.protocol; + +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; + +public class ThurayaProtocolDecoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + var decoder = inject(new ThurayaProtocolDecoder(null)); + + verifyPositions(decoder, binary( + "235400437101072bca3c0201348b9a00014c9f085493fc02200c5411470000000042323a300001348b9a00014d03085493ea02200c5010000000000042323a3000f2c1")); + + verifyPosition(decoder, binary( + "2354002b5101072bca3c01348b9a00013fba000000000000000010000000000042323a3000174f4e00f9de")); + + verifyNull(decoder, binary( + "235400d88115071e37d691030133342e3233362e3133302e3637000000001e56313030320030000700080102030405060708020101010101020201030103030302020000007800000078000004b000001c20050a64000015b3800015b374657374696e67003132333435360002010f28393031303539383938303134373738000043383a592c43373a592c43333a592c43323a592c43313a592c42353a592c42343a592c42323a592c42313a592c41323a592c41313a590045313a592c45373a590065746973616c61742e61650047455400322e3130d6de")); + + } + +} diff --git a/src/test/java/org/traccar/protocol/Tk102ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tk102ProtocolDecoderTest.java index 2185fda29..2c495d09d 100644 --- a/src/test/java/org/traccar/protocol/Tk102ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Tk102ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Tk102ProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class Tk102ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Tk102ProtocolDecoder(null); + var decoder = inject(new Tk102ProtocolDecoder(null)); verifyNull(decoder, buffer( "[\u00800000000000\u000821315452]")); diff --git a/src/test/java/org/traccar/protocol/Tk103FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Tk103FrameDecoderTest.java index 57df2ac5b..a3cfa24cf 100644 --- a/src/test/java/org/traccar/protocol/Tk103FrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Tk103FrameDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Tk103FrameDecoderTest extends ProtocolTest { @@ -9,7 +9,7 @@ public class Tk103FrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Tk103FrameDecoder(); + var decoder = inject(new Tk103FrameDecoder()); verifyFrame( binary("283836343735353535353535353535352C445733422C3133313131372C412C353536322E30323837304E2C30313334382E3038313934452C312E3539372C3232333730372C3239312E36352C2D302E31302C3429"), diff --git a/src/test/java/org/traccar/protocol/Tk103ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tk103ProtocolDecoderTest.java index 321d0cb50..a3b3fa86b 100644 --- a/src/test/java/org/traccar/protocol/Tk103ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Tk103ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,20 @@ public class Tk103ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Tk103ProtocolDecoder(null); + var decoder = inject(new Tk103ProtocolDecoder(null)); + + verifyAttributes(decoder, text( + "(007030201454BS5190:02150000753001DC,91:0EE8060EDC0A01DC,92:42014201DC0A01DC,93:00010127000037C8,94:0E01000002000000,95:020EE10EE20EE800030EE40EE00EE700040EDD0EE40EE400050EDC0EDF0EE400,96:0142000000000000,97:0000000000000000,98:0000000000000000)")); + + verifyAttribute(decoder, text( + "(352602014867BS500064FF0EF10FF10FF00FF20FF30FF20FF20FF40FF20FF40FF40FF20FF30FF20F0000000000000000000000000000000000000000000000001663000000010004000000000000000002444444420000000000A00FA000000000000000200000000315E2000000)"), + "batteryTemp2", 26); + + verifyAttributes(decoder, text( + "(027046434858BZ00,{460,0,20949,58711}\n{460,0,20494,54003}\n{460,0,20951,19569}\n,01000000)")); + + verifyAttributes(decoder, text( + "(027045009305BP05355227045009305,{413,2,30073,16724}\n{413,2,30073,16730}\n{413,2,30073,49860}\n,01000000)")); verifyPosition(decoder, text( "(868822040452227,DW3B,150421,A,4154.51607N,45.78950E,0.050,103142,0.000,595.200,7,0)")); diff --git a/src/test/java/org/traccar/protocol/Tk103ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Tk103ProtocolEncoderTest.java index c34674128..57ebbaec1 100644 --- a/src/test/java/org/traccar/protocol/Tk103ProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/Tk103ProtocolEncoderTest.java @@ -1,17 +1,17 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class Tk103ProtocolEncoderTest extends ProtocolTest { @Test - public void testEncodeOutputControl() { + public void testEncodeOutputControl() throws Exception { - var encoder = new Tk103ProtocolEncoder(null); + var encoder = inject(new Tk103ProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); @@ -23,9 +23,9 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest { } @Test - public void testEncodeEngineStop() { + public void testEncodeEngineStop() throws Exception { - var encoder = new Tk103ProtocolEncoder(null); + var encoder = inject(new Tk103ProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); @@ -36,9 +36,9 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest { } @Test - public void testEncodePositionSingle() { + public void testEncodePositionSingle() throws Exception { - var encoder = new Tk103ProtocolEncoder(null); + var encoder = inject(new Tk103ProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); @@ -49,9 +49,9 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest { } @Test - public void testEncodePositionPeriodic() { + public void testEncodePositionPeriodic() throws Exception { - var encoder = new Tk103ProtocolEncoder(null); + var encoder = inject(new Tk103ProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); @@ -63,9 +63,9 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest { } @Test - public void testEncodePositionStop() { + public void testEncodePositionStop() throws Exception { - var encoder = new Tk103ProtocolEncoder(null); + var encoder = inject(new Tk103ProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); @@ -76,9 +76,9 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest { } @Test - public void testEncodeGetVersion() { + public void testEncodeGetVersion() throws Exception { - var encoder = new Tk103ProtocolEncoder(null); + var encoder = inject(new Tk103ProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); @@ -89,9 +89,9 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest { } @Test - public void testEncodeRebootDevice() { + public void testEncodeRebootDevice() throws Exception { - var encoder = new Tk103ProtocolEncoder(null); + var encoder = inject(new Tk103ProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); @@ -102,9 +102,9 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest { } @Test - public void testEncodeSetOdometer() { + public void testEncodeSetOdometer() throws Exception { - var encoder = new Tk103ProtocolEncoder(null); + var encoder = inject(new Tk103ProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); @@ -115,9 +115,9 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest { } @Test - public void testEncodePositionSingleAlternative() { + public void testEncodePositionSingleAlternative() throws Exception { - var encoder = new Tk103ProtocolEncoder(null, true); + var encoder = inject(new Tk103ProtocolEncoder(null, true)); Command command = new Command(); command.setDeviceId(1); @@ -128,9 +128,9 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest { } @Test - public void testEncodePositionPeriodicAlternative() { + public void testEncodePositionPeriodicAlternative() throws Exception { - var encoder = new Tk103ProtocolEncoder(null, true); + var encoder = inject(new Tk103ProtocolEncoder(null, true)); Command command = new Command(); command.setDeviceId(1); @@ -141,9 +141,9 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest { } @Test - public void testEncodePositionStopAlternative() { + public void testEncodePositionStopAlternative() throws Exception { - var encoder = new Tk103ProtocolEncoder(null, true); + var encoder = inject(new Tk103ProtocolEncoder(null, true)); Command command = new Command(); command.setDeviceId(1); @@ -154,9 +154,9 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest { } @Test - public void testEncodeGetVersionAlternative() { + public void testEncodeGetVersionAlternative() throws Exception { - var encoder = new Tk103ProtocolEncoder(null, true); + var encoder = inject(new Tk103ProtocolEncoder(null, true)); Command command = new Command(); command.setDeviceId(1); @@ -167,9 +167,9 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest { } @Test - public void testEncodeRebootDeviceAlternative() { + public void testEncodeRebootDeviceAlternative() throws Exception { - var encoder = new Tk103ProtocolEncoder(null, true); + var encoder = inject(new Tk103ProtocolEncoder(null, true)); Command command = new Command(); command.setDeviceId(1); @@ -180,9 +180,9 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest { } @Test - public void testEncodeIdentificationAlternative() { + public void testEncodeIdentificationAlternative() throws Exception { - var encoder = new Tk103ProtocolEncoder(null, true); + var encoder = inject(new Tk103ProtocolEncoder(null, true)); Command command = new Command(); command.setDeviceId(1); @@ -193,9 +193,9 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest { } @Test - public void testEncodeSosOnAlternative() { + public void testEncodeSosOnAlternative() throws Exception { - var encoder = new Tk103ProtocolEncoder(null, true); + var encoder = inject(new Tk103ProtocolEncoder(null, true)); Command command = new Command(); command.setDeviceId(1); @@ -207,9 +207,9 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest { } @Test - public void testEncodeSosOffAlternative() { + public void testEncodeSosOffAlternative() throws Exception { - var encoder = new Tk103ProtocolEncoder(null, true); + var encoder = inject(new Tk103ProtocolEncoder(null, true)); Command command = new Command(); command.setDeviceId(1); @@ -221,9 +221,9 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest { } @Test - public void testEncodeCustom() { + public void testEncodeCustom() throws Exception { - var encoder = new Tk103ProtocolEncoder(null); + var encoder = inject(new Tk103ProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); @@ -235,9 +235,9 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest { } @Test - public void testEncodeCustomAlternative() { + public void testEncodeCustomAlternative() throws Exception { - var encoder = new Tk103ProtocolEncoder(null, true); + var encoder = inject(new Tk103ProtocolEncoder(null, true)); Command command = new Command(); command.setDeviceId(1); @@ -249,9 +249,9 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest { } @Test - public void testEncodeSetConnectionAlternative() { + public void testEncodeSetConnectionAlternative() throws Exception { - var encoder = new Tk103ProtocolEncoder(null, true); + var encoder = inject(new Tk103ProtocolEncoder(null, true)); Command command = new Command(); command.setDeviceId(1); @@ -264,14 +264,14 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest { } @Test - public void testEncodeSosNumberAlternative() { + public void testEncodeSosNumberAlternative() throws Exception { - var encoder = new Tk103ProtocolEncoder(null, true); + var encoder = inject(new Tk103ProtocolEncoder(null, true)); Command command = new Command(); command.setDeviceId(1); command.setType(Command.TYPE_SOS_NUMBER); - command.set(Command.KEY_INDEX, "0"); + command.set(Command.KEY_INDEX, 0); command.set(Command.KEY_PHONE, "+55555555555"); command.set(Command.KEY_DEVICE_PASSWORD, "232323"); diff --git a/src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java index b747fa960..46dc031a3 100644 --- a/src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,7 @@ public class Tlt2hProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Tlt2hProtocolDecoder(null); + var decoder = inject(new Tlt2hProtocolDecoder(null)); verifyNull(decoder, text( "#860517049471362#MT700#0000#AUTO#1\r\n", @@ -81,6 +81,13 @@ public class Tlt2hProtocolDecoderTest extends ProtocolTest { "#357671031289215#V600#0000#AUTOLOW#1\r\n", "#00735e1c$GPRMC,115647.000,A,5553.6524,N,02632.3128,E,0.00,0.0,130614,0.0,W,A*28")); + verifyPositions(decoder, false, text( + "#860186058100000#MT700#0000#AUTO#1\r\n", + "#39#262,03,8CE6,A672$WIFI,154928.00,A,-74,3CA62F52615B,-82,A0E4CB83852D,,,050123*28\r\n##\r\n")); + + verifyPositions(decoder, false, text( + "#860186058100000#MT700#0000#AUTO#1\r\n", + "#39#262,03,8CE6,A672$GPRMC,115419.00,V,,,,,,,050123,,,A*7D\r\n##\r\n")); } } diff --git a/src/test/java/org/traccar/protocol/TlvProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TlvProtocolDecoderTest.java index 54d66dac9..547014752 100644 --- a/src/test/java/org/traccar/protocol/TlvProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TlvProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TlvProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class TlvProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new TlvProtocolDecoder(null); + var decoder = inject(new TlvProtocolDecoder(null)); verifyNull(decoder, binary( "30430f383630323437303330303934333931ff10393233323132323030303834353433340f533636385f415f56312e30315f454eff1130303a30433a45373a30303a30303a30300132")); diff --git a/src/test/java/org/traccar/protocol/TmgFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TmgFrameDecoderTest.java index 886470745..fe9ac26f1 100644 --- a/src/test/java/org/traccar/protocol/TmgFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TmgFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TmgFrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class TmgFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new TmgFrameDecoder(); + var decoder = inject(new TmgFrameDecoder()); verifyNull( decoder.decode(null, null, binary("2424242424"))); diff --git a/src/test/java/org/traccar/protocol/TmgProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TmgProtocolDecoderTest.java index 722df5306..12402e068 100644 --- a/src/test/java/org/traccar/protocol/TmgProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TmgProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TmgProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class TmgProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new TmgProtocolDecoder(null); + var decoder = inject(new TmgProtocolDecoder(null)); verifyPosition(decoder, text( "$loc,869309013800417,08032014,094459,1,2826.1956,N,07659.7690,E,0.0,2.5,4441,31,6,95,1,LLLL,NNTN,HH,0.15,0.26,HR38AU1389,0,SW0.1a")); diff --git a/src/test/java/org/traccar/protocol/TopflytechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TopflytechProtocolDecoderTest.java index dbc7210e0..67b04446e 100644 --- a/src/test/java/org/traccar/protocol/TopflytechProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TopflytechProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TopflytechProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class TopflytechProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new TopflytechProtocolDecoder(null); + var decoder = inject(new TopflytechProtocolDecoder(null)); verifyPosition(decoder, text( "(880316890094910BP00XG00b600000000L00074b54S00000000R0C0F0014000100f0130531152205A0706.1395S11024.0965E000.0251.25")); diff --git a/src/test/java/org/traccar/protocol/TopinProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TopinProtocolDecoderTest.java index e8da93006..b55e12e9d 100644 --- a/src/test/java/org/traccar/protocol/TopinProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TopinProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,7 @@ public class TopinProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new TopinProtocolDecoder(null); + var decoder = inject(new TopinProtocolDecoder(null)); verifyNull(decoder, binary( "787801080D0A")); @@ -17,6 +17,9 @@ public class TopinProtocolDecoderTest extends ProtocolTest { verifyNull(decoder, binary( "78780d0103593390754169634d0d0a")); + verifyNotNull(decoder, binary( + "787803181604130318491475905bd30e25001e10bbf7635d14759006e626560401cc00000028660090df425f000028660090df576c00002866009487566700002866009ca15667000d0a")); + verifyAttribute(decoder, binary( "7878006921120412565802010601071e4a9764071e4a9864010d0a"), Position.KEY_ALARM, Position.ALARM_VIBRATION); diff --git a/src/test/java/org/traccar/protocol/TopinProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/TopinProtocolEncoderTest.java index d3ff13941..b6ba9b0eb 100644 --- a/src/test/java/org/traccar/protocol/TopinProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/TopinProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; @@ -9,7 +9,7 @@ public class TopinProtocolEncoderTest extends ProtocolTest { @Test public void testEncode() throws Exception { - var encoder = new TopinProtocolEncoder(null); + var encoder = inject(new TopinProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/TotemFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TotemFrameDecoderTest.java index 95949c51b..5eff60e51 100644 --- a/src/test/java/org/traccar/protocol/TotemFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TotemFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TotemFrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class TotemFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new TotemFrameDecoder(); + var decoder = inject(new TotemFrameDecoder()); verifyFrame( binary("24243030323542423836323031303033373239343836313345"), diff --git a/src/test/java/org/traccar/protocol/TotemProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TotemProtocolDecoderTest.java index e16774782..949e69275 100644 --- a/src/test/java/org/traccar/protocol/TotemProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TotemProtocolDecoderTest.java @@ -1,14 +1,22 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; +import org.traccar.model.Position; public class TotemProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new TotemProtocolDecoder(null); + var decoder = inject(new TotemProtocolDecoder(null)); + + verifyAttribute(decoder, text( + "$$0494E2123456789012345|150425223945,113.925525,22.55814,1122334455|38"), + Position.KEY_DRIVER_UNIQUE_ID, "1122334455"); + + verifyPosition(decoder, text( + "$$0111AA353081090067318|0804400022070722520240400005B364ED5003107300001.700000002245.3919N10231.6952W000001860E")); verifyPosition(decoder, text( "$$0112E5864606045334223|201112223514,-68.923106,-22.455926,$Cloud,1738,621,730,12100,0,0,255,0,40,40,0,0,255,|13")); diff --git a/src/test/java/org/traccar/protocol/TotemProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/TotemProtocolEncoderTest.java index a4fca2d9e..795f75842 100644 --- a/src/test/java/org/traccar/protocol/TotemProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/TotemProtocolEncoderTest.java @@ -1,17 +1,17 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class TotemProtocolEncoderTest extends ProtocolTest { @Test public void testEncode() throws Exception { - var encoder = new TotemProtocolEncoder(null); + var encoder = inject(new TotemProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(2); @@ -25,7 +25,7 @@ public class TotemProtocolEncoderTest extends ProtocolTest { @Test public void testSmsEncode() throws Exception { - var encoder = new TotemProtocolSmsEncoder(null); + var encoder = inject(new TotemProtocolSmsEncoder(null)); Command command = new Command(); command.setDeviceId(2); diff --git a/src/test/java/org/traccar/protocol/Tr20ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tr20ProtocolDecoderTest.java index def280b04..1323691c6 100644 --- a/src/test/java/org/traccar/protocol/Tr20ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Tr20ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Tr20ProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,10 @@ public class Tr20ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Tr20ProtocolDecoder(null); + var decoder = inject(new Tr20ProtocolDecoder(null)); + + verifyPosition(decoder, text( + "%%m13,L,221221103115,N1237.2271W00801.9500,000,000,B13.1:F0.0,04020000,253,CFG:133.00|")); verifyPosition(decoder, text( "%%TR20GRANT,L,210602170135,N0951.1733W08356.7672,000,000,C80:F0,00020008,108,CFG:6980.00|")); diff --git a/src/test/java/org/traccar/protocol/Tr900ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tr900ProtocolDecoderTest.java index d3d35b356..636f2101a 100644 --- a/src/test/java/org/traccar/protocol/Tr900ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Tr900ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Tr900ProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class Tr900ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Tr900ProtocolDecoder(null); + var decoder = inject(new Tr900ProtocolDecoder(null)); verifyPosition(decoder, text( ">00001001,4,1,150626,131252,W05830.2978,S3137.2783,,00,348,18,00,003-000,0,3,11111011*3b!"), diff --git a/src/test/java/org/traccar/protocol/TrackboxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TrackboxProtocolDecoderTest.java index caac520e1..514ef2647 100644 --- a/src/test/java/org/traccar/protocol/TrackboxProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TrackboxProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TrackboxProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class TrackboxProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new TrackboxProtocolDecoder(null); + var decoder = inject(new TrackboxProtocolDecoder(null)); verifyNull(decoder, text( "a=connect&v=11&i=111111111111111")); diff --git a/src/test/java/org/traccar/protocol/TrakMateProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TrakMateProtocolDecoderTest.java index ef99527d7..55ccd2f0b 100644 --- a/src/test/java/org/traccar/protocol/TrakMateProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TrakMateProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TrakMateProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class TrakMateProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new TrakMateProtocolDecoder(null); + var decoder = inject(new TrakMateProtocolDecoder(null)); verifyPosition(decoder, text( "^TMSTP|352984083995323|116|13.07809|77.55979|131508|131118|0.0|146.51|7|0|71 -2 248|0|13.1|0.0|10.5|1|0|0|0|#")); diff --git a/src/test/java/org/traccar/protocol/TramigoFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TramigoFrameDecoderTest.java index 80e6a6e81..8c88f0ebb 100644 --- a/src/test/java/org/traccar/protocol/TramigoFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TramigoFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TramigoFrameDecoderTest extends ProtocolTest { @@ -8,7 +8,11 @@ public class TramigoFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new TramigoFrameDecoder(); + var decoder = inject(new TramigoFrameDecoder()); + + verifyFrame( + binary("0480001df35b1b69101a023ef34f0090436d38003200380e0000850081c0e4ff6d542f00000015000000050000000000007600a20100008f436d3800014400000000000000000021000a0006005a574a6169726f7320486972692043656e747265205072696d617279205363686f6f6c536f7574686572746f6e486172617265"), + decoder.decode(null, null, binary("0480001df35b1b69101a023ef34f0090436d38003200380e0000850081c0e4ff6d542f00000015000000050000000000007600a20100008f436d3800014400000000000000000021000a0006005a574a6169726f7320486972692043656e747265205072696d617279205363686f6f6c536f7574686572746f6e486172617265"))); verifyFrame( binary("8000ed2bb0009c000101bee000050b09633d925b5472616d69676f3a205472697020737461727465642c2053686f636b2053656e736f722c206174204b696e6720437265656b20526f61642d46726565746f776e205374726565742c20506f727420486172636f7572742c205269766572732c204e472c20342e37363336312c20372e30313836382c2030373a31383a333620536570203320454f46"), diff --git a/src/test/java/org/traccar/protocol/TramigoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TramigoProtocolDecoderTest.java index 8b556b356..dc0ca2b73 100644 --- a/src/test/java/org/traccar/protocol/TramigoProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TramigoProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TramigoProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,13 @@ public class TramigoProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new TramigoProtocolDecoder(null); + var decoder = inject(new TramigoProtocolDecoder(null)); + + verifyNull(decoder, binary( + "04d000a9e45c1b69101a023ef34f00549eec63047795ec63000000004eff5c0062c2e4ffbf612f00ce9aec63000000007700960180c0e4ff5f542f004c1200007b004a023d0200000001430000000000000000001f000b0006005a57436f63612d436f6c6120426f74746c696e6720506c616e74204861726172654772616e69746573696465486172617265014400000000000000000021000a0006005a574a6169726f7320486972692043656e747265205072696d617279205363686f6f6c536f7574686572746f6e486172617265")); + + verifyPosition(decoder, binary( + "0480001df35b1b69101a023ef34f0090436d38003200380e0000850081c0e4ff6d542f00000015000000050000000000007600a20100008f436d3800014400000000000000000021000a0006005a574a6169726f7320486972692043656e747265205072696d617279205363686f6f6c536f7574686572746f6e486172617265")); verifyAttributes(decoder, binary( "8000c426b000a6000101c557037598050d5c8a595472616d69676f3a204d6f76696e672c20302e3132206b6d2045206f66204c617275742054696e2049736c616d6963205072696d617279205363686f6f6c2c2054616970696e672c20506572616b2c204d592c20342e38333134392c203130302e37333038352c204e572077697468207370656564203130206b6d2f682c2030303a34393a30382041756720392020454f46")); diff --git a/src/test/java/org/traccar/protocol/TranSyncProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TranSyncProtocolDecoderTest.java new file mode 100644 index 000000000..27f53b574 --- /dev/null +++ b/src/test/java/org/traccar/protocol/TranSyncProtocolDecoderTest.java @@ -0,0 +1,21 @@ +package org.traccar.protocol; + +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; + +public class TranSyncProtocolDecoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + var decoder = inject(new TranSyncProtocolDecoder(null)); + + verifyPosition(decoder, binary( + "3a3a2b583f086065705154043900801017050b11190f01623ef40887dff00000c25e9ff707000007152a2d0000000105004794916902050000100000050252ee060200822323")); + + verifyAttributes(decoder, binary( + "3a3a2b583f086065705154043900801017050b11190f01623ef40887dff00000c25e9ff707000007152a2d0000000105004794916902050000000000050252ee060200822323")); + + } + +} diff --git a/src/test/java/org/traccar/protocol/TrvProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TrvProtocolDecoderTest.java index 33d4e0124..31cb5b36d 100644 --- a/src/test/java/org/traccar/protocol/TrvProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TrvProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TrvProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class TrvProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new TrvProtocolDecoder(null); + var decoder = inject(new TrvProtocolDecoder(null)); verifyNull(decoder, text( "TRVAP00352121088015548")); diff --git a/src/test/java/org/traccar/protocol/Tt8850ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tt8850ProtocolDecoderTest.java index 5d0e86a3f..e471671f8 100644 --- a/src/test/java/org/traccar/protocol/Tt8850ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Tt8850ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Tt8850ProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class Tt8850ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Tt8850ProtocolDecoder(null); + var decoder = inject(new Tt8850ProtocolDecoder(null)); verifyPosition(decoder, text( "\u0000\u0004,007F,0,GTFRI,020102,867844000667538,4142726856,0,0,1,3,1.6,0,997.3,-66.830786,10.483394,20171212171418,0734,0004,041A,4220,69,20171212171657,FF61")); diff --git a/src/test/java/org/traccar/protocol/TytanProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TytanProtocolDecoderTest.java index 1cab0f8b1..d42a249e8 100644 --- a/src/test/java/org/traccar/protocol/TytanProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TytanProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TytanProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class TytanProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new TytanProtocolDecoder(null); + var decoder = inject(new TytanProtocolDecoder(null)); verifyPositions(decoder, binary( "B500192000001405125652CA9B1A325FC98D11A9990018020118FC0D")); diff --git a/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java index fba8f7db4..d90e5e07e 100644 --- a/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java @@ -1,14 +1,22 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; +import org.traccar.model.Position; public class TzoneProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new TzoneProtocolDecoder(null); + var decoder = inject(new TzoneProtocolDecoder(null)); + + verifyAttribute(decoder, binary( + "545a00d424240153011300000863835029944118170316023b180016040485c73d2479187e170316023b1800000000060c000000000d1cc0406303019904aa00000000008a012520205e544f4e474c4f4d245049544f4f4e244d522e5e5e3f3b363030373634333132303130303134323234323d3139303631393538313032363d3f2b2020202020202020202020202032322020202020202020202020203120202020202020202020202030303234363238202031303730302020202020202020202020202020202020202020203f00030080000006e80e0d0a"), + Position.KEY_CARD, "% ^TONGLOM$PITOON$MR.^^?;6007643120100142242=190619581026=?+ 22 1 0024628 10700 ?"); + + verifyAttributes(decoder, binary( + "545a003724240407020200000180322000001610160b151019100000000c010a07320101088600007dca000baa102837016a0114025500000169e80d0a")); verifyAttributes(decoder, binary( "545a004d24240407010d0000018032100000031515090c052c2100000022030a033400201347000056860a03340020134700002feb0a03340020134700007d96000baa10211f01810127022d000001ebe00d0a")); diff --git a/src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java b/src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java index 77afbb50f..9b10328f6 100644 --- a/src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class UlbotechFrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class UlbotechFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new UlbotechFrameDecoder(); + var decoder = inject(new UlbotechFrameDecoder()); verifyFrame( binary("f8010103515810532780699f7e2e3f010e015ee4c906bde45c00000000008b0304004000000404002c776005060373193622110b00240b00fee8ffff807dffff606d0b00fee9af000000af0000000b00feee7d78807dffffffff100101cc2af8"), diff --git a/src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java index b7c6d2d45..5d2692a8a 100644 --- a/src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class UlbotechProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class UlbotechProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new UlbotechProtocolDecoder(null); + var decoder = inject(new UlbotechProtocolDecoder(null)); verifyPosition(decoder, binary( "f801010353323083177450a703f6f0010efe55a31a0923d01400050070007003040a42000004040070cca00506039b1876220f060800000000000000000725310553410c0c9e310d05310f4641100440311119411f00476101810f8000310487411f00480804203a14c009033320159310f8")); diff --git a/src/test/java/org/traccar/protocol/UlbotechProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/UlbotechProtocolEncoderTest.java index 288f8dc70..0ee1516ba 100644 --- a/src/test/java/org/traccar/protocol/UlbotechProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/UlbotechProtocolEncoderTest.java @@ -1,15 +1,15 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; public class UlbotechProtocolEncoderTest extends ProtocolTest { @Test - public void testEncode() { + public void testEncode() throws Exception { - var encoder = new UlbotechProtocolEncoder(null); + var encoder = inject(new UlbotechProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/UproProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/UproProtocolDecoderTest.java index 8c12f48f7..c99166374 100644 --- a/src/test/java/org/traccar/protocol/UproProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/UproProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,7 @@ public class UproProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new UproProtocolDecoder(null); + var decoder = inject(new UproProtocolDecoder(null)); verifyPosition(decoder, buffer( "*HQ201999999,BA&A1656512233362911356523660000230618&B0100060010&C00000<6<&F0000&R2405&V0109&W0000003E&K00100&T65&I54600027A00FCB6227A00FCA5727A00E955327A00E8B5327A00F9748&Y54600027A000000FCB6227A000000FCA5727A000000E955327A000000E8B5327A000000F9748&b00A7E81007607#")); @@ -53,7 +53,7 @@ public class UproProtocolDecoderTest extends ProtocolTest { verifyPosition(decoder, buffer( "*MG201693502000034964,AB&A0800253335360507036975710000091116&P0730000032d2a94d&B0000000000&N13&Z12&U_P\0\0\0\u0004\0\0\0\0\0\0\0\0\0\0"), - position("2016-11-09 08:00:25.000", true, -33.58934, -70.61626)); + position("2016-11-09 08:00:25.000", false, -33.58934, -70.61626)); verifyNull(decoder, buffer( "*MG20113800138000,AH")); @@ -69,7 +69,7 @@ public class UproProtocolDecoderTest extends ProtocolTest { verifyPosition(decoder, buffer( "*AI200905300036,AH&A0317264913209801844913060000251115&B0500000000&C0;4?72:9&F0000"), - position("2015-11-25 03:17:26.000", false, 49.22016, 18.74855)); + position("2015-11-25 03:17:26.000", true, 49.22016, 18.74855)); verifyPosition(decoder, buffer( "*AI2000905300036,AS&A1647304913209801844913060000251115&B0400000000&C0;4?72:9&F0000")); diff --git a/src/test/java/org/traccar/protocol/UuxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/UuxProtocolDecoderTest.java index 753063d26..492f241e5 100644 --- a/src/test/java/org/traccar/protocol/UuxProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/UuxProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class UuxProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class UuxProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new UuxProtocolDecoder(null); + var decoder = inject(new UuxProtocolDecoder(null)); verifyNull(decoder, binary( "81910b01ff")); diff --git a/src/test/java/org/traccar/protocol/V680ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/V680ProtocolDecoderTest.java index 59b6dbea9..d8e3f1f53 100644 --- a/src/test/java/org/traccar/protocol/V680ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/V680ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class V680ProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class V680ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new V680ProtocolDecoder(null); + var decoder = inject(new V680ProtocolDecoder(null)); verifyPosition(decoder, text( "#867967020910610#01234567890#1#0000#AUT#1#0500000000120000#114.036291,E,22.665795,N,111.00,000.00#111116#193333##"), diff --git a/src/test/java/org/traccar/protocol/VisiontekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/VisiontekProtocolDecoderTest.java index b079346c9..ff43a94ab 100644 --- a/src/test/java/org/traccar/protocol/VisiontekProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/VisiontekProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class VisiontekProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class VisiontekProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new VisiontekProtocolDecoder(null); + var decoder = inject(new VisiontekProtocolDecoder(null)); verifyPosition(decoder, text( "$1,117,28,01,16,15,05,48,1725.0518N,07824.5298E,0620,11,0,185,2062,0,0,0,1,1,1,1,24,00.0000,00.3740,00.0000,VAJRA V1.00,A")); diff --git a/src/test/java/org/traccar/protocol/VltProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/VltProtocolDecoderTest.java new file mode 100644 index 000000000..e0e88b324 --- /dev/null +++ b/src/test/java/org/traccar/protocol/VltProtocolDecoderTest.java @@ -0,0 +1,22 @@ +package org.traccar.protocol; + +import io.netty.handler.codec.http.HttpMethod; +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; + +public class VltProtocolDecoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + var decoder = inject(new VltProtocolDecoder(null)); + + verifyPosition(decoder, request(HttpMethod.POST, "/", + buffer("vltdata=NRM12345678901234501L1060418102230023.125503N080.068033E4041231234123456789070.48120.5025273011M"))); + + verifyPositions(decoder, request(HttpMethod.POST, "/", + buffer("vltdata=BTH86123004167306301301L1240323181909009.226018N076.794980E404x19601d000037596000.00198.7013011401S02H1240323181807009.226018N076.794980E404x72090a000000000000.00198.7013011101S02H1240323181707009.226018N076.794980E404x72090a000014598000.00198.7013011101S02H1240323181605009.226018N076.794982E404x72090a000014596000.00198.7013011101S02H1240323181504009.226018N076.794982E404x72090a000014596000.00198.7013010901S02H1240323181402009.226018N076.794980E404x72090a000014596001.67198.0013021301S02H1240323181306009.226010N076.794980E404x72090a000014596000.00174.0013021401S02H1240323181155009.226010N076.794980E404x72090a088511008000.00174.0013011201S02H1240323181057009.226010N076.794980E404x72090a000014596000.00174.0013011201S02H1240323180958009.226010N076.794980E404x72090a000014596000.00174.0013021401S02H1240323180858009.226010N076.794980E404x72090a000014596001.48174.0013021201S02H1240323180755009.226005N076.794982E404x72090a000014598000.00164.4013011301S02H1240323180652009.226005N076.794982E404x72090a000014598000.00164.4013021101S"))); + + } + +} diff --git a/src/test/java/org/traccar/protocol/VnetProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/VnetProtocolDecoderTest.java index d43176a45..ead1624d8 100644 --- a/src/test/java/org/traccar/protocol/VnetProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/VnetProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class VnetProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class VnetProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new VnetProtocolDecoder(null); + var decoder = inject(new VnetProtocolDecoder(null)); verifyNull(decoder, binary( "24240000140029111909062986818303379282604c452e322e30302ea32b020f0000d3552323")); diff --git a/src/test/java/org/traccar/protocol/Vt200FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Vt200FrameDecoderTest.java index 91cc3d317..a869e7dc1 100644 --- a/src/test/java/org/traccar/protocol/Vt200FrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Vt200FrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Vt200FrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class Vt200FrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Vt200FrameDecoder(); + var decoder = inject(new Vt200FrameDecoder()); verifyFrame( binary("28631037309456208400340102dc0906171616454415760201144494473f920a0c0000030500200100417c1f383a9d1090510000006a00007000000e00180ee129"), diff --git a/src/test/java/org/traccar/protocol/Vt200ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Vt200ProtocolDecoderTest.java index 791751c27..c4533aa11 100644 --- a/src/test/java/org/traccar/protocol/Vt200ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Vt200ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -9,7 +9,7 @@ public class Vt200ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Vt200ProtocolDecoder(null); + var decoder = inject(new Vt200ProtocolDecoder(null)); verifyPosition(decoder, binary( "28192030961807208200210101b919011818375801245774036424612500160917000003aa008800007b00aa3429")); diff --git a/src/test/java/org/traccar/protocol/VtfmsFrameDecoderTest.java b/src/test/java/org/traccar/protocol/VtfmsFrameDecoderTest.java index c03511160..18baf75cb 100644 --- a/src/test/java/org/traccar/protocol/VtfmsFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/VtfmsFrameDecoderTest.java @@ -1,16 +1,16 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class VtfmsFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new VtfmsFrameDecoder(); + var decoder = inject(new VtfmsFrameDecoder()); assertEquals( buffer("(863071010087648,0HK44,00,000,14,2,9,,A,114946,180313,11.0244,076.9768,282,000,00000,00000,K,0000128,1,12.8,,200,2.501,,4.001,0,0,0,0,0,0,0,,)105"), diff --git a/src/test/java/org/traccar/protocol/VtfmsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/VtfmsProtocolDecoderTest.java index 1d4fa7f21..8ca1b9df5 100644 --- a/src/test/java/org/traccar/protocol/VtfmsProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/VtfmsProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class VtfmsProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class VtfmsProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new VtfmsProtocolDecoder(null); + var decoder = inject(new VtfmsProtocolDecoder(null)); verifyPosition(decoder, text( "(861359037432331,0EF87,00,0,21,2,01,,A,154559,230119,1101.4046,07656.3859,241,000,00078,00000,K,0000812,1,12.7,,,,,,1,0,0,0,1,1,1,+919566531111*+919994462226,)054"), diff --git a/src/test/java/org/traccar/protocol/WatchFrameDecoderTest.java b/src/test/java/org/traccar/protocol/WatchFrameDecoderTest.java index 91bccb4c0..cb20e1b4d 100644 --- a/src/test/java/org/traccar/protocol/WatchFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/WatchFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class WatchFrameDecoderTest extends ProtocolTest { @@ -8,7 +8,15 @@ public class WatchFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new WatchFrameDecoder(); + var decoder = inject(new WatchFrameDecoder()); + + verifyFrame( + binary("5b33472a393730353134313734302a303030392a4c4b2c302c302c35335d"), + decoder.decode(null, null, binary("5b33472a393730353134313734302a303030392a4c4b2c302c302c35335d5b33472a393730353134313734302a303035412a55442c3139303732332c3139303730372c412c33362e3831353130392c4e2c31302e313739323331322c452c382e32342c3132372e392c32312e302c352c3130302c35332c302c302c30303030303030302c302c302c35382e305d5b33472a393730353134313734302a303030332a544b515d5b33472a393730353134313734302a303030392a4c4b2c302c302c35335d"))); + + verifyFrame( + binary("5b33472a3838303930303234322a303133442a55442c3132303632332c3134303032302c412c34382e3934393237332c4e2c20342e333738333036302c452c31382e35362c34332e382c302e302c31322c3130302c37362c3232363132302c302c30303030303030302c322c3235352c3230342c382c333131302c35353032352c3134362c333133302c34393239372c3132342c352c42616e67696e67576966692c33343a61313a65643a65313a39313a34662c2d37312c42415220576946692c33363a61323a65313a65643a61313a64652c2d37322c4e6574776f726b576966692c32363a64653a61313a65643a65313a61302c2d37332c46696265722c33363a61313a65643a65313a39313a34662c2d37352c5b4c475f57616c6c2d4d6f756e7420412f435d653732352c36363a61313a65643a65313a65373a32352c2d38322c31352e305d"), + decoder.decode(null, null, binary("5b33472a3838303930303234322a303133442a55442c3132303632332c3134303032302c412c34382e3934393237332c4e2c20342e333738333036302c452c31382e35362c34332e382c302e302c31322c3130302c37362c3232363132302c302c30303030303030302c322c3235352c3230342c382c333131302c35353032352c3134362c333133302c34393239372c3132342c352c42616e67696e67576966692c33343a61313a65643a65313a39313a34662c2d37312c42415220576946692c33363a61323a65313a65643a61313a64652c2d37322c4e6574776f726b576966692c32363a64653a61313a65643a65313a61302c2d37332c46696265722c33363a61313a65643a65313a39313a34662c2d37352c5b4c475f57616c6c2d4d6f756e7420412f435d653732352c36363a61313a65643a65313a65373a32352c2d38322c31352e305d"))); verifyFrame( binary("5b33472a3335323636313039303134333135302a303030412a4c4b2c302c302c3130305d"), diff --git a/src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java index 4fab19f26..5fd0ede44 100644 --- a/src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java @@ -1,19 +1,48 @@ package org.traccar.protocol; -import io.netty.buffer.ByteBuf; -import org.junit.Test; -import org.traccar.Context; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; +import org.traccar.database.MediaManager; import org.traccar.model.Position; -import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class WatchProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new WatchProtocolDecoder(null); + var decoder = inject(new WatchProtocolDecoder(null)); + + verifyAttribute(decoder, buffer( + "[3G*9705141740*000B*oxygen,0,98]"), + "bloodOxygen", 98); + + verifyPosition(decoder, buffer( + "[3G*9705141740*00C2*UD_LTE,260723,185105,V,00.000000,,00.0000000,,0.00,0.0,0.0,0,100,67,0,0,00000000,2,0,605,1,10006,65799,14,10020,4104,4,3,,34:60:f9:ec:19:f8,-82,,98:48:27:55:18:20,-96,,34:e8:94:e4:06:18,-104,0.0]")); + + verifyPosition(decoder, buffer( + "[SG*9059011020*0067*AL,240123,181628,V,54.427538,N,6.409275,W,0.00,0,0,0,19,90,0,0,00000000,1,1,234,10,55C0,3B882A2,132,,10]")); + + verifyPosition(decoder, buffer( + "[SG*9059011020*006b*UD2,240123,162011,A,54.427621,N,6.409190,W,0.00,0,0,8,19,88,0,0,00000000,1,1,FFFF,FFFF,FFFE,3B882A2,132,,00]")); + + verifyAttribute(decoder, buffer( + "[ZJ*5678901234*0001*0009*TEMP,36.5]"), + Position.PREFIX_TEMP + 1, 36.5); + + verifyAttribute(decoder, buffer( + "[ZJ*689466020014198*0003*0113*AL,221121,085515,V,00.000000,N,000.000000,E,0,0,0,0,0,44,0,0,00100000,1,255,460,0,16399,234887445,0,6,WIFI00,68:77:24:1b:e7:a7,-59,WIFI01,68:77:24:1b:e3:30,-75,WIFI02,68:77:24:1b:e3:27,-75,WIFI03,00:41:d2:c0:f2:f1,-76,WIFI04,00:41:d2:c0:f2:f0,-77,WIFI05,68:77:24:1b:e3:d8,-82]"), + Position.KEY_ALARM, Position.ALARM_REMOVING); + + verifyNull(decoder, binary( + "5b5a4a2a3738393436383035303034323639322a303033342a303433392a4a58544b2c302c77617463685f375f32303232303532363039333935342c312c362c2321414d520a0c0a3c3f96d98367e9468ea245320c0a3c3f96d98367e9468ea245320c0a3c3f96d98367e9468ea245320c389814ffcd762fe49d50ae7a2e0cb528aefbf76911df05c2fbe17d050c2200cff77ef0df4d9b4ab9a4340c449814dbe7c63fa82bc3750d800cc48abbffddb0df8e8fda95e5980c49982ff6cf65f9377d02a39c3aaa0c389805f2ff42c1b80e0a0eb1dc0c2998e9defe15cfa3bdbe80d3540c7298c2f6d9239e3eae3c4a81660c490034dbfd513fedad0c2fc3900cc40039b7f71bb0657ba75558c40cd7813b97ff7219777ec7f401260c7d040003bff6f75fdb898a6ba1140cecd127f7f83357cb73a68a5f680cb081d4f6e749e6af8ed367bc480cec1815dfffd2dbed358112af320ccc21179fffbd17a3a61c133b380c920047defc72a784770ec0fe400c383c42f6faebe76fa736e9d1be0c4918e7decddc67ec9afd87ff220cc418e6bffe6cf6c9ac1f83c3900ca6cad1ffe3da24e1be3b547d03c00c6b00b8fee77f60a76d3e7d0292e20c2918b7bfff76387b793d3a36300c7d0400b7fff7f63ae513ac6f74de0c980016fbff7d01f9b30fca67c7220caa982ff6dbd7bf3d8dfed143ec0c44982fdfcb7b517e26f6ea52420c0ed19cfedb438f179d3fc50ec40c008ad2ffff635fe28dc1ec1f860c76cb7d05bffda53eaebe4d201ae20ccccaffbbfd3db4abb6ddf39d0e0c6b00acfeff406fe4a12661caf80c76982fbffffeb17b2f65472f300c7d04c24bf6ef1b10e47f73fc10b40c76036cfecd4e7837145a6a8e900ccccaf2beeba36fbaa36feb60640c7d0400dfffff73bfa3b87d0256a1700c7d04e4efd6efc23b651eb73e77780ccc1813ffffb39f6baa6f3195080c00007afffee0b9df6346ca08d00c6bca7d04feff4d64a7b56f9855da0c769819fbfd954ea5bd7d040ba6420c4c4c09bfffd5f6c57d01930e7d01220cc48a22dffe30bfcbaf08a795140c7d040433b7ff3cba5f3e336a40060cecbd2bfaf775bea7bde7e095ee0cc4caaef7fb623feeaab69eabc20c8c0021bffd1ea4a1b090175a920c7d046445fbdfc1abe910b0ca56160c7d0440cbd7ef03893c7f7d02e1a8c20c85e42dff5fdaa145759d326b5a0ccc4084f75f8eeb7b15e4eb4ff80c6bc22ef7d7eaf8df3b8ff678cc0cca4026f3cf7518fd731ceca3560cec041ffbe7655eaf822f04c4fe0c7d0478e3fbfcb5dfc8a81fdbb9e20cc418e6d7ff777f26affe37cc020cd7977cbff7d0aecb9727be6a0c0c00bde1f2d797fc8754ed09d4ae0cc498279f7e729fcb9eff70c1a60c7d0478e5bfffc67eef953fda69aa0c7200f1bbf7e891ef5a287cb5b80c983629fee5555f739f8a29279a0c76d103bef73f55a6bf89ba8ce40cc46424f6ded9a194167a067d05880cc4780efeffc37fae944d89bfb40c4464bffffb6dd54e8344394a5c0c49781ffffc653feaa31af59ac00cc464cebfd76ae4e8a55d5b5a4a2a3738393436383035303034323639322a303033352a303434332a4a58544b2c302c77617463685f375f32303232303532363039333935342c322c362c5f24fbbc0c7d04983effcfe35fbd8fff7ce6f60c448a1dfbd715bec3b42448e58a0c0e0421bf5f17ebc31a43301bc40c768ababf4f66cf629ee31fe9900c6b6426bf5f4eb7669dacc439140c945733bff9351e7d04ab6be54ef60ccc98bfbffbf67f63b78d8f42880c7d0400c6f7fffab5cbae8e8275da0c0097ffdefff5dfafb0c4727d04980ccc78d1f6ff1a6b7e37631059fa0c760045f3fd853fea877d03aa87440cc40022f7ffa8b5c58f6e80c58e0ccc4c1f9ffff32fa7af87de04560cb09801f6fdd32efcbdad9495b60c6b987d05bff72d61eb687d05a9f9e20cca57c7ffff6cb1fe3387ea596e0c629823dedfbfc50b97965e44ea0c4918ccbfd7cc75e59fff92e9ee0c8c78e7faff707ffda60ec3ada60c30c217befffcb8f3290f06d75e0c0e8a7bb7dff3eec7b7928ef6680c38ca1cdeff272ba012597d051a520c0018dfffeff27d01e369b3bcd7d80c98146eb2fc7f45c38e1ce53e120c3d5700ff7fd44bee28aa089d900c007157bffffe30302d7d0583585c0ccc5747bfe7f73cff7d02de1098240c7d047865f6feca71d70bdfaab6a20cecb946bfd966be74b61a06c6660c441612ffe7eb966a8c2886cf760ccc7827bffd653ff7980a62e9320c00ca98f7fc1b311a3986700cc20c490017bfff70cfe7bb455637320c6228afbf7e6da4c59763b693740cccc2b5fee1951975760e4a9dca0c0000faffdb255f6f86af4be6fc0cccc2ffdfff7d0225efbc847c42760cb48a8fdffbe23ff1a78782cbe60c7d04980cffff6e6dfc75a6c65a500ce62808bfff24f853748a9537400cb4004ebfeb6e70aa4314903e8c0cb46408ff72d62f44945f64897d040cccca7abfff2db5c6bfcc345e700c7d04c24dd6fd5f95638f78974a800cc4caffffffb17d01c36bdd7d047c2e0c7d0436d1f7f8917f83af7fc5c2180c070449f6fe570f128577c8c97d040c7d047813f6df3fd17d026b6790d1b80cc400c9d6d92dc56497843ed10a0c44986effffe13fef8eef7c717a0c7d049872bbfdad3abe2be0d60b240c4900adbf7d02dfa4ef960fdf23580c629845fefc5a650fbf8b0d4cda0c7d040076b7ef273fe78fce983ae40c4a00ded7ff629f7fab4531501c0c6200b7ffedf13fe6a68f01fb6c0cc49809f77fdb91517fb39e8ba00c760009bf7fd6afe7b46921f27a0cecbd1df6faf99120776e807d03fe0c98ca23fbff3749fb5ed3909c160cc44cbbbfffbd70e77d0367bc2e740c499816f77fe2ffe08edd7d048c9e0c940021beff1df0f1338c2f1a3a0c7d040083feebba1c756e5760c2200c94981dfffdf921b92b99909ba40c7d04984ebbdf7ff0c77d025a1fbfc00c7657b3ff77f4befcae2bf625640c62ca53f7fdb35f2e92af3bd5c60c7d049878bbdfc0dfc1bdaa1918000cc404bbbfefb41ee6b50a39bf180c767874f7fb8ee156320ee600020c69981cffffe0fff6bb2764acaa0cec781bfbff7d0324eb9b4b4d5d5b5a4a2a3738393436383035303034323639322a303033362a303433302a4a58544b2c302c77617463685f375f32303232303532363039333935342c332c362c73580c07ca4fbbfff5bec5abcfac465c0c0042a8beff316aed38fe4603dc0c7d04577d04fefdc929f7294ce7520c0c76984dfaff402fe68ebe113b000c7d0436a6fed465ff4f8d8e3306300c44e434f7f79e84abb8fa081be80c983636bef7e128bf7d03d34c71c80cd7982dffd54cac94536beab1320c058bd6bfff701fe69d1a39d1e80c76caedb77ffea0e47febb469440c0098b2bef5e55eed981672b1c40cd800d1bef4bda58daf7d01d309180c08cadffbfc31799f5613fab2da0c7d049828beffa5eea0bd09a14c2e0c0000e59feb5f9dff7f4945e98e0cc40046ffcfe57fb58f7d053dd9e20c7d040047d7ffd38f67aa9db8d38c0c7698d2df5fe2efc6bb0f5a18220c980367bbfd95dae73e3bf2b59a0c49813796fdf5efab9d3ed7d3d80c9800ebd7fdb831ae8f91811e920c7d04ca74bbff5cdcf45605c25f140c013e029fbfe7ffc299376b409a0c7510009e7d02010f68824cccead60cb13b00f7d5821e6c8d594a06880c75ec00f6e782e8f06b0c610d1a0cc09843dfef117fa5b3aef236f40cdcc208ffff4831f83703ca20c00cc09800bf7fd71eec9ba34e3c200c650f6bffd5bc4dbe7ec5b0f1d80c0d1c1f92ef917fa0b96023eac00c8efb1db6bfa42eed9f69051d4e0c8eff23b2fe4486d56ef44f702e0c8ebc1b9657d46eeea852ac337e0c8efb1f96d9877fc9b34c38f1540c121c2796df13577c727f8cedec0ca9107d05afdd6a9d957ab76c02560c121c2dd6bfd33fa5b844f27d02340cb11037b6e3962e6eaf52d6e7460c2b1c3db2dfe61eef9aad3dcc0a0c542e42d763e709fe723a83ae7c0c7cfa56bf7fedb8763c3f8ab2c00ca40022efff2411604d22a485e00c228adcbfdfcd748293c66b0bf20cb10f0097ff806dcf7a0a640d6e0c5a0400b7e3030ef4ae0a6046f80cb82812fe7b04d77e4bab11894a0c541c24b2dd53fea5891e3824420ca9fb26b2d7156fa485049cac420ca91c2d9edb2487526c2e2260fa0ca9bc25b65ff28e41a34232c0f00c8efb25b649e51ec39c0ae6703a0c70f425a6c820dfaf2f8124c8960c80f42794b323aed9ca9b6924be0cb93b278e3e8d929c36bb70b6360c587b27524f85857d056d214841660cda2f278b4c876eb3186e5acc540ce6832f6e3f0d82f81226e6dc3a0c0b922feff50a03d03c98ae28360c03d62befb54ab611de0c8addee0c84d626b7fdb4461556dc0153040c0b0f27ff5781c5c2b32f6762140c49bf2dfaf521969b702ecb11d80c7d050f27cfed75bfab95dc4af3180c011c3d8e7451df80a5cd0948080c7c2e35f2c7b0becca22e50769e0c7c1c3d965e628f46a9a58ae6100c7c2e3b965f4a6b5ebd1436b0b00c7c863eaedf18c7816cdeeed7e20c2b3e3df6b73952b2b1abc048a00c7cfb3db6cd67ae68cda18af9d60c2bff3e96df12ff61b39627cd580c7c863fd6cd810ee595d85ee6645d5b5a4a2a3738393436383035303034323639322a303033372a303433372a4a58544b2c302c77617463685f375f32303232303532363039333935342c342c362c0c7cff3fb65f812fe196dc62d8360c7c2e3db2de738fae9ca7c7236c0c2b2e3d96df6c1d775a3df5a47e0c7c3e3f96fe943dc07e81e170700c75503fb7d6a6197f1b08df85440c5a133ab6d6a0e68f3acb04b7b00cfa293fd6cfc20c390d5265d3c80c581347b278077d03dc22d9e49c680ce64d47924e8dcdffe9be19dc100cb90f4f9e612d2201961c2e111c0c003b4febb58a96c809ab5a71040ce6d759fbddba54b2aa9d9d983e0cc029576ecf1f703099d84994520c4029579e7d0281577073f5629dd80c5a2952b6fed177177d01e57d0175c20cd8647d044f7e3c58aba523ddb0720cb56753d3e9c6799f95938401f60c756414ff5f00fb57bc4d7e111c0c7c085573ef743ee19ca30057e00c752e5ab77e34eec69f3cbe53940c542e4d96d6e7ff03854a9506620c7cff55b6efca197610a86606180c7cfb55b67eb25f4b999c002bf20c7c107d03beeb451fad8d042492740cb8f05eb5ff664f679c1cc991c60ce1671bffd33d9445b57d0584c7d60c07781dfef5e3ee81b45ff8a8c80cc40072b7dfe6fe6e978257253a0cb1131fe6783035868e439b00d80c583b27b17ecb53dd165c3422180c703b2fbeb18d023e14d90304700c3cce2b93cdc2e6b3550a5546160cb13b3b72fe24273859059b52040c224d34edc742f6bb7c877d02b5940cb84d37cdcfcbc3f03a041cf4ac0ca4923a9f3b74e805b235cce8660c759000fbfd90565072aa7d03bb520cb592017f411dcbe80a0cc420360c09d6528fcf2e25b382ee46d75a0ce2e853d3cfb617fd7d023f05e0780c07cc3bbabfa9f6cec5aa48ac600c404208fb678a4cf16a9c09098c0cf14244fbdfc2afe7932fe57d02900c011c369e7d0264a8570a54f9a2e80cb1103daed6249fe59dc888c2ac0cb1103573c5f697be759923ecc80c705f7d059effaaa4b09b976f375a0c804223bbed89767d041693edab180cb49819d7eb5a8573bb10c951540c3c643af74f021f25b6dd3b7d02040c07982bd73fb31fe38f09a227a40c037610ff7d03c6058098fef936a80c03b808debbe055a1a71319128c0c6b8f2e9e3bb52d71c226d3141a0cdab82d7fa9d416167a1cb950020c03c32bb23ee9e3743f3b6853060cc02937adb5157d05f4a6960c1b680c4076365e6b754bda9394a7aa720c06293ef23313fb1b94bcc2c09a0ce676006bbd8e0417941b90d23c0cb9294f7d01fd3d65748cf6d1cc220c03767d035e3a65e6f2514ab1db060ce6d965bf4035f65c79a9e4446a0ce62921dbf46457b56cd65f6c500c584d6bba7ea8166edd6cf7693a0cb939227bfca25f82a7c4202f2e0c06ce2b9a4f0d95f39eb43c22840c06901eef6fd15fe48f452000e00c62023afbffb269beba0ee0ac540ce6ec00f37b8139f2026e80f1440c22501dfe7d017d03ed8c3ff6803f240cc48ae3b7fd725e63a347cd64100c0094f8efdeb0a9f35c83eb06b00c005d5b5a4a2a3738393436383035303034323639322a303033382a303433332a4a58544b2c302c77617463685f375f32303232303532363039333935342c352c362c6647deef2df57fb68fd76b660c44e43cbfefc0beec867d025c3f920cb4507d01ff6f42ee6c1f58a0db0e0c7d045701ff7f19f9f90fdff037c20c4404259efcd47d02a52b7ef6df0e0c7d04d128f3578db14779993109560ce1e41ecffd2851af49256b1ecc0c7d049837ffff707168db3dd1ac240cc09419f7f39a7d013d6ec1b4e55c0cc4881cff5f49f55fadc52285a40c06ce9ad7dbb8349b97031ac3b00c407d049d9bed74dfadad950eab560c06799ffac7a924b18bdf11c3d40c8054a2f777522e65be921a77240c8042a19bff706fe5b7e3e7d0360cd82840bff3f4fe429bea0081560c0064419fd9157f8b9c797e92c20cc47fe2b7d5e6ee45bf0524010e0c0894299e7d037e4553bcbbc0989e0c40501bbbdc926f8daf53885e0a0c80971ffee559f5bf98ed54cf140c00581abbffac75b88b14d5e3780c5a046dd66fd7bfe5b7ca749df40c403672bbcfe47ee797860ded5e0cb5502efb7ee2ffc7bbdc27d0880c44ca08fbf7031ecd9321d21ef40cdc9806fefde36f0699ad1569420c22cb1bfbdc974fcb900ab601440c00cb1cfbdfd2f7d2730b0b46680c0b4203dfef309fe0adc121e2080c225829feeb901f25b926343bae0c624204dfff4875b69ceda4df4c0c499886ffe7301e8e8bf9ef0a260cdce408dbef829fe45337e98d860c010800ef76088d8015f3b2c84e0c755c3ecfc898f50fbcdb9344220c7c7d03459349337d01e4949da1b4ec0c7cf046ae6e4a90df3552070a5e0c7cf045d23fbba0951147986eca0c7536426e6ef2ef0b7d01c22fd2960cb8284e9db92d40552988334f200cb8024acdbfea90d13b545fe35e0cb8f04d5e48659d48b97c1096140c7c084f79df4402702126bf358a0cb8a74d4def66535e145638d12e0c543e4d8edfa2531b15a340eb800cb83e4eafbd9031291a8563e4780c54a74ef1c7300da79aa94bb67d050cb8f04eba6fb0325813871511b80ce20ff6fffd404ed58ecf264c8e0c7c0f1cb3fe1a36fb7187c379d00c7c3e52f6727d0261ca8bdd1361b80cb8d64d9a4fb1cfe0aadaaa7d05c40c2b2e44b2bfb9b4dcad9d7d0200280c7c3b47d5db803fc2aee71a77f00c7c1343b2bfa06ee6ad3228f10a0c7c3b4a76477fe7650b0c809ca80c54d74b92dbc327134514d6126e0c7c5f4a8f4f74efaaa5cd0a43bc0c7c104bb64ff5ce604fa2028b680c7cff4aaec7f7a9394cc4c599e60cb8ce52eeb553d215a6a466b4120c750f4fae5f9816c04a0a3f751e0c75e04f73c58e96bb4b925a4c680cb80f4b6e3fe1635614bbdeccbc0c7c3b4faf7e825e42d473ee76800ce298578ffcb61f0af936387bbe0cb800e39bf62410681583e0d2b20cb4987d05ff7f297bf69eaf4cd8b40cb57815bf7f300fcffe9fae966a0cb497acd75fd37f648d00d141a00cc48af9fffe1a3b979b8f8573e80c29bff1bf4b94c6fd7c3eec075e0c7cce1f5d5b5a4a2a3738393436383035303034323639322a303033392a303065352a4a58544b2c302c77617463685f375f32303232303532363039333935342c362c362c8f5cf9f34fa6edceb18c0ca4ce436e7d02d329de94b63eb11a0cb894438f44cf753dbc1a83b4560cb8e44391eff729d4155a231bf40cb59443f373b4288492cd6e38160c5a42049bfec8678ad42bce0c560cb12e39fec3d1fe24b5572ee0be0cb1ce35fe33688455aa74e3ffa40cb1d635df4b704160ae864a7d03440cb10f3267dbf4a21d540ca1051c0cb11337724f8747f5547d054e91660cb1a73692df964f3bbb0758d41a0cfe05b60c314460297eae4185f20cad673ceb6dfa05ddbe0278d5de5d")); + + verifyPosition(decoder, buffer( + "[3G*9031853319*004E*UD2,220322,055105,A,22.761162,N,114.360192,E,0,0,47,14,100,64,0,0,00000008,0,0]")); verifyAttribute(decoder, buffer( "[3G*2104326058*000E*btemp2,1,35.29]"), @@ -134,22 +163,26 @@ public class WatchProtocolDecoderTest extends ProtocolTest { verifyPosition(decoder, buffer( "[ZJ*014111001350304*0038*008a*UD,070318,021027,V,00.000000,N,000.000000,E,0,0,0,0,100,18,1000,50,00000000,4,255,460,0,9346,5223,42,9346,5214,20,9784,4083,11,9346,5221,5]")); + + verifyPosition(decoder, buffer( + "[3G*8800000015*00DD*UD,010120,025946,V,0.0,N,0.0,E,22.0,0,-1,0,100,98,0,0,00000000,0,5,eduroam,f4:db:e6:d2:a8:00,-53,eduroam,f4:db:e6:da:d0:80,-79,eduroam,78:0c:f0:24:f9:80,-82,Lions,b0:be:76:0a:05:9a,-82,tubs-guest,f4:db:e6:d2:a8:01,-53,0.0]")); } @Test public void testDecodeVoiceMessage() throws Exception { - var decoder = new WatchProtocolDecoder(null); - - verifyNull(decoder.decode(null, null, buffer("[CS*1234567890*0004*TK,1]"))); + var decoder = inject(new WatchProtocolDecoder(null)); - ByteBuf data = binary("7d5b5d2c2aff"); + var mediaManager = mock(MediaManager.class); + when(mediaManager.writeFile(any(), any(), any())).thenReturn("mock.amr"); + decoder.setMediaManager(mediaManager); - Object decodedObject = decoder.decode(null, null, concatenateBuffers(buffer("[CS*1234567890*000e*TK,#!AMR"), data.resetReaderIndex(), buffer("]"))); - assertEquals("1234567890/mock.amr", ((Position) decodedObject).getAttributes().get("audio")); + verifyAttribute(decoder, concatenateBuffers( + buffer("[CS*1234567890*000e*TK,#!AMR"), binary("7d5b5d2c2aff"), buffer("]")), + Position.KEY_AUDIO, "mock.amr"); - verifyFrame(concatenateBuffers(buffer("#!AMR"), data.resetReaderIndex()), ((MockMediaManager) Context.getMediaManager()).readFile("1234567890/mock.amr")); + verify(mediaManager).writeFile(any(), any(), any()); } diff --git a/src/test/java/org/traccar/protocol/WatchProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/WatchProtocolEncoderTest.java index bcfc8df41..649c0016b 100644 --- a/src/test/java/org/traccar/protocol/WatchProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/WatchProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; @@ -9,7 +9,7 @@ public class WatchProtocolEncoderTest extends ProtocolTest { @Test public void testEncode() throws Exception { - var encoder = new WatchProtocolEncoder(null); + var encoder = inject(new WatchProtocolEncoder(null)); Command command; @@ -58,9 +58,9 @@ public class WatchProtocolEncoderTest extends ProtocolTest { } @Test - public void testEncodeTimezone() { + public void testEncodeTimezone() throws Exception { - var encoder = new WatchProtocolEncoder(null); + var encoder = inject(new WatchProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/WialonProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/WialonProtocolDecoderTest.java index 12724c32f..b7c422456 100644 --- a/src/test/java/org/traccar/protocol/WialonProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/WialonProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class WialonProtocolDecoderTest extends ProtocolTest { @@ -8,11 +8,15 @@ public class WialonProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new WialonProtocolDecoder(null); + var decoder = inject(new WialonProtocolDecoder(null)); verifyNull(decoder, text( "#L#2.0;42001300083;;CE45")); + verifyAttribute(decoder, text( + "#D#220323;114150;2234.80479;N;11354.87786;E;0;NA;59;11;NA;NA;NA;;NA;d_battr:1:94,d_csq:1:21,di_light:1:1;E7C9"), + "di_light", 1.0); + verifyAttributes(decoder, text( "#D#NA;NA;5429.681944502211763;N;02654.60403650999069;E;NA;NA;NA;NA;NA;NA;NA;1.0;NA;m1:1:9196679,d1:1:15397,t1:1:20,b1:1:162,fuel1:2:21588.0,pv1:2:35.98,finish:1:1;0x9b0")); @@ -75,6 +79,9 @@ public class WialonProtocolDecoderTest extends ProtocolTest { verifyPositions(decoder, text( "#B#110315;045857;5364.0167;N;06127.8262;E;0;155;965;7;2.40;4;0;14.77,0.02,3.6;AB45DF01145;")); + verifyAttribute(decoder, text( + "#D#120319;112003;NA;NA;NA;NA;0.000;NA;NA;0;NA;NA;NA;NA;NA;motion:3:false"), + "motion", false); } } diff --git a/src/test/java/org/traccar/protocol/WliFrameDecoderTest.java b/src/test/java/org/traccar/protocol/WliFrameDecoderTest.java index 2e0d86a61..405fc340e 100644 --- a/src/test/java/org/traccar/protocol/WliFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/WliFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class WliFrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class WliFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new WliFrameDecoder(); + var decoder = inject(new WliFrameDecoder()); verifyFrame( binary("0231000101bba758c900010034000500ff001001258fc9013e80ed00001183350101e20006003200090030000a0032000b003331000c0031000d00343438000e003530000f003100100031303800130032001b003134001c0033392c33352c32382c33382c34302c33372c33332c33382c33352c34322c33372c3335001d003130001e0038002300343235002400303100250031303432320026003336343733002700323800280037002900312c312c312c312c31302e3232352e3135312e3230342c36353533352c302c39392c39392c3235352c3235352c3235352c323535002a0030002c0030003000300032003000330031003400ff001c0214130502061b0101258fc9013e80ed000001e2000000000000004d004500302c323031392f30352f30322c30363a33333a30302c323031392f30352f30322c30363a32363a3230005a003000f100352c302c342c302c2d312c2d3100f2003300f3003100f50038363634323530333137303639323400f600312c302c302c3431322c3000f70038343437373200f80032312c31312c302c302c302c302c2c2c2c2c2c302c3000f9003300fa00393100fb0032313100fc0032313000ff00313535363737383533340003"), diff --git a/src/test/java/org/traccar/protocol/WliProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/WliProtocolDecoderTest.java index 6f86a9fd9..482c15da0 100644 --- a/src/test/java/org/traccar/protocol/WliProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/WliProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class WliProtocolDecoderTest extends ProtocolTest { @@ -8,11 +8,14 @@ public class WliProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new WliProtocolDecoder(null); + var decoder = inject(new WliProtocolDecoder(null)); verifyNull(decoder, binary( "0232776c693a30343930333332303332343103")); + verifyAttributes(decoder, binary( + "0231000101536c27e40001003400dbd20030363a34323a303000dbd30030352f30322f31390004002d3100060030000a00343235000b003031000c003130343232000d003336343733000e003239000f0037001000312c312c312c312c31302e3230322e33342e33312c36353533352c302c39392c39392c3235352c3235352c3235352c323535001100300013003000140033001500343237001600333700170031001800313038001900320031003000320030004500302c323031392f30352f30322c30363a34313a34312c323031392f30352f30322c30363a33353a303800f100342c302c332c302c2d312c2d3100f2003300f3003100f50038363634323530333137303639323400f600312c302c302c3431322c3000f70038343437373200f800302c3330302c302c302c302c302c2c2c2c2c2c302c3000f9003300fa00393100fb0032313100fc0032313000ff00313535363737393332300003")); + verifyPosition(decoder, binary( "0231000101bba758c900010034000500ff001001258fc9013e80ed00001183350101e20006003200090030000a0032000b003331000c0031000d00343438000e003530000f003100100031303800130032001b003134001c0033392c33352c32382c33382c34302c33372c33332c33382c33352c34322c33372c3335001d003130001e0038002300343235002400303100250031303432320026003336343733002700323800280037002900312c312c312c312c31302e3232352e3135312e3230342c36353533352c302c39392c39392c3235352c3235352c3235352c323535002a0030002c0030003000300032003000330031003400ff001c0214130502061b0101258fc9013e80ed000001e2000000000000004d004500302c323031392f30352f30322c30363a33333a30302c323031392f30352f30322c30363a32363a3230005a003000f100352c302c342c302c2d312c2d3100f2003300f3003100f50038363634323530333137303639323400f600312c302c302c3431322c3000f70038343437373200f80032312c31312c302c302c302c302c2c2c2c2c2c302c3000f9003300fa00393100fb0032313100fc0032313000ff00313535363737383533340003")); diff --git a/src/test/java/org/traccar/protocol/WondexFrameDecoderTest.java b/src/test/java/org/traccar/protocol/WondexFrameDecoderTest.java index 85a878aef..f1f65cb2c 100644 --- a/src/test/java/org/traccar/protocol/WondexFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/WondexFrameDecoderTest.java @@ -1,17 +1,17 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; public class WondexFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new WondexFrameDecoder(); + var decoder = inject(new WondexFrameDecoder()); assertNull( decoder.decode(null, null, binary("f0d70b0001ca9a3b"))); diff --git a/src/test/java/org/traccar/protocol/WondexProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/WondexProtocolDecoderTest.java index 111cc9fbe..8f86263c4 100644 --- a/src/test/java/org/traccar/protocol/WondexProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/WondexProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class WondexProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class WondexProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new WondexProtocolDecoder(null); + var decoder = inject(new WondexProtocolDecoder(null)); verifyPosition(decoder, buffer( "2005010051,19990925063830,26.106181,44.440225,0,0,0,7,2,0.0,0,,,0")); diff --git a/src/test/java/org/traccar/protocol/WondexProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/WondexProtocolEncoderTest.java index ed0af844e..2e1a45c8c 100644 --- a/src/test/java/org/traccar/protocol/WondexProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/WondexProtocolEncoderTest.java @@ -1,16 +1,16 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class WondexProtocolEncoderTest extends ProtocolTest { @Test public void testEncode() throws Exception { - var encoder = new WondexProtocolEncoder(null); + var encoder = inject(new WondexProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(2); diff --git a/src/test/java/org/traccar/protocol/WristbandProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/WristbandProtocolDecoderTest.java index 10381168e..ba40486a6 100644 --- a/src/test/java/org/traccar/protocol/WristbandProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/WristbandProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class WristbandProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class WristbandProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new WristbandProtocolDecoder(null); + var decoder = inject(new WristbandProtocolDecoder(null)); verifyNotNull(decoder, binary( "000102004459583836383730343034343735303035357c56312e307c317c7b4630342331382c30372c332c3539303139322c33303a31382c30372c332c3539303139322c33307d0d0afffefc")); diff --git a/src/test/java/org/traccar/protocol/Xexun2FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Xexun2FrameDecoderTest.java index aeca95376..9e909f1ca 100644 --- a/src/test/java/org/traccar/protocol/Xexun2FrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Xexun2FrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Xexun2FrameDecoderTest extends ProtocolTest { @@ -8,12 +8,16 @@ public class Xexun2FrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Xexun2FrameDecoder(); + var decoder = inject(new Xexun2FrameDecoder()); verifyFrame( binary("faaf0014000286147503139003400032f2b001002f4260b0d6a0008019104a3378323130333135317c323130333132303100704020308715758089502023015648643670faaf"), decoder.decode(null, null, binary("faaf0014000286147503139003400032f2b001002f4260b0d6a0008019104a3378323130333135317c323130333132303100704020308715758089502023015648643670faaf"))); + verifyFrame( + binary("FAAF123456FAAF123456FBBF123456FAAF"), + decoder.decode(null, null, binary("FAAF123456FBBF01123456FBBF02123456FAAF"))); + } } diff --git a/src/test/java/org/traccar/protocol/Xexun2FrameEncoderTest.java b/src/test/java/org/traccar/protocol/Xexun2FrameEncoderTest.java new file mode 100644 index 000000000..27e894705 --- /dev/null +++ b/src/test/java/org/traccar/protocol/Xexun2FrameEncoderTest.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 Xexun2FrameEncoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + Xexun2FrameEncoder encoder = new Xexun2FrameEncoder(); + + ByteBuf result = Unpooled.buffer(); + encoder.encode(null, binary("FAAF123456FAAF123456FBBF123456FAAF"), result); + verifyFrame(binary("FAAF123456FBBF01123456FBBF02123456FAAF"), result); + + } + +} diff --git a/src/test/java/org/traccar/protocol/Xexun2ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Xexun2ProtocolDecoderTest.java index 9faccec01..b373c8283 100644 --- a/src/test/java/org/traccar/protocol/Xexun2ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Xexun2ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Xexun2ProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class Xexun2ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Xexun2ProtocolDecoder(null); + var decoder = inject(new Xexun2ProtocolDecoder(null)); verifyPositions(decoder, false, binary( "faaf00140a5a8618810536243350005ed8e101005b64622880401b001482060864cc2296f840daa22aa884f008c87483c291efddc4f09fc2f49db3c058ef68005a9abe1ae8299d6449bac4e984e0c1d6baa8469d265ff2b60100cc00080000fb2e0013572a3600000002000000000000faaf")); @@ -25,6 +25,10 @@ public class Xexun2ProtocolDecoderTest extends ProtocolTest { verifyPositions(decoder, binary( "FAAF00140CF18626490454584530002BF2DD0200130013D360EFD7F514006402010D46322C4A450BA026D460EFD7FA14006402010D46322C4A450BA026FAAF")); + verifyPositions(decoder, binary( + "FAAF0014000C8622050512345670002DF3A001002A0062D9047400005E0280001E47001B400D4BA732DF505E40B4153AAF78FEF00109000000000042B36666FAAF"), + position("2022-07-21 07:47:00.000", true, 51.68715, 0.06103)); + } } diff --git a/src/test/java/org/traccar/protocol/Xexun2ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Xexun2ProtocolEncoderTest.java new file mode 100644 index 000000000..62d511c0b --- /dev/null +++ b/src/test/java/org/traccar/protocol/Xexun2ProtocolEncoderTest.java @@ -0,0 +1,29 @@ +package org.traccar.protocol; + +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; +import org.traccar.model.Command; + +public class Xexun2ProtocolEncoderTest extends ProtocolTest { + + @Test + public void testEncode() throws Exception { + + var encoder = inject(new Xexun2ProtocolEncoder(null)); + + Command command; + + command = new Command(); + command.setDeviceId(1); + command.setType(Command.TYPE_POWER_OFF); + verifyCommand(encoder, command, binary("FAAF0007000112345678901234500004FEBC6F663D31FAAF")); + + command = new Command(); + command.setDeviceId(1); + command.setType(Command.TYPE_POSITION_PERIODIC); + command.set(Command.KEY_FREQUENCY, 150); + verifyCommand(encoder, command, binary("FAAF0007000112345678901234500015F90E747261636B696E675F73656E643D3135302C313530FAAF")); + + } + +} diff --git a/src/test/java/org/traccar/protocol/XexunFrameDecoderTest.java b/src/test/java/org/traccar/protocol/XexunFrameDecoderTest.java index 43b8bdc0e..82287f6ad 100644 --- a/src/test/java/org/traccar/protocol/XexunFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/XexunFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class XexunFrameDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class XexunFrameDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new XexunFrameDecoder(); + var decoder = inject(new XexunFrameDecoder()); verifyFrame( binary("4750524d432c3230353933352e3030302c412c353134302e343335302c4e2c3530312e303638362c452c302e30302c302e30302c3132313031352c30302c303030302e302c412a37302c462c2c696d65693a3335393538373031343731383339322c"), diff --git a/src/test/java/org/traccar/protocol/XexunProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/XexunProtocolDecoderTest.java index d2caa9c55..7331827f0 100644 --- a/src/test/java/org/traccar/protocol/XexunProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/XexunProtocolDecoderTest.java @@ -1,14 +1,14 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class XexunProtocolDecoderTest extends ProtocolTest { @Test - public void testDecode() throws Exception { + public void testDecodeSimple() throws Exception { - var decoder = new XexunProtocolDecoder(null, false); + var decoder = inject(new XexunProtocolDecoder(null, false)); verifyAttributes(decoder, text( "GPRMC,.000,A,0.000000,S,0.0000,W,0.00,0.00,,00,0000.0,A*55,L,,imei:353579010727036,")); @@ -53,7 +53,12 @@ public class XexunProtocolDecoderTest extends ProtocolTest { verifyPosition(decoder, text( "GPRMC,043435.000,A,811.299200,S,11339.9500,E,0.93,29.52,160313,00,0000.0,A*65,F,,imei:359585014597923,")); - decoder = new XexunProtocolDecoder(null, true); + } + + @Test + public void testDecodeFull() throws Exception { + + var decoder = inject(new XexunProtocolDecoder(null, true)); verifyPosition(decoder, text( "171007160505,,GPRMC,160505.000,A,5323.4680,N,00252.4202,W,000.0,129.7,071017,,,A*7A,F,ACCStart, imei:864504031916915,10,41.1,F:4.28V,1,135,19824,234,15,0062,B7D5")); diff --git a/src/test/java/org/traccar/protocol/XirgoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/XirgoProtocolDecoderTest.java index f3a56a79d..8f912d9e2 100644 --- a/src/test/java/org/traccar/protocol/XirgoProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/XirgoProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class XirgoProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class XirgoProtocolDecoderTest extends ProtocolTest { @Test public void testDecodeCustom() throws Exception { - var decoder = new XirgoProtocolDecoder(null); + var decoder = inject(new XirgoProtocolDecoder(null)); decoder.setForm("UID,EV,D,T,LT,LN,AL,GSPT,HD,SV,HP,BV,CQ,GS,SI,IG,OT"); @@ -31,7 +31,7 @@ public class XirgoProtocolDecoderTest extends ProtocolTest { @Test public void testDecodeNew() throws Exception { - var decoder = new XirgoProtocolDecoder(null); + var decoder = inject(new XirgoProtocolDecoder(null)); verifyPosition(decoder, text( "$$352054058132185,4001,2017/04/21,00:01:05,32.54659,-116.90670,143.2,0,0,0,598,0.0,12,0.9,765840,7.0,14.5,19,1,1,0011,8.5,63.2,5,21999,184,255,671,207,100,185##")); @@ -59,7 +59,7 @@ public class XirgoProtocolDecoderTest extends ProtocolTest { @Test public void testDecodeOld() throws Exception { - var decoder = new XirgoProtocolDecoder(null); + var decoder = inject(new XirgoProtocolDecoder(null)); verifyPosition(decoder, text( "$$354660046140722,6001,2013/01/22,15:36:18,25.80907,-80.32531,7.1,19,165.2,11,0.8,11.1,17,1,1,3.9,2##"), diff --git a/src/test/java/org/traccar/protocol/XirgoProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/XirgoProtocolEncoderTest.java index 4f01aa72b..2efedf51a 100644 --- a/src/test/java/org/traccar/protocol/XirgoProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/XirgoProtocolEncoderTest.java @@ -1,17 +1,17 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class XirgoProtocolEncoderTest extends ProtocolTest { @Test public void testEncode() throws Exception { - var encoder = new XirgoProtocolEncoder(null); + var encoder = inject(new XirgoProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/Xrb28ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Xrb28ProtocolDecoderTest.java index 3197ba854..fae163a56 100644 --- a/src/test/java/org/traccar/protocol/Xrb28ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Xrb28ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Xrb28ProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class Xrb28ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Xrb28ProtocolDecoder(null); + var decoder = inject(new Xrb28ProtocolDecoder(null)); verifyAttributes(decoder, text( "*SCOR,OM,123456789123456,Q0,412,80,28#")); diff --git a/src/test/java/org/traccar/protocol/Xrb28ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Xrb28ProtocolEncoderTest.java index 357da8f7b..eaa29a833 100644 --- a/src/test/java/org/traccar/protocol/Xrb28ProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/Xrb28ProtocolEncoderTest.java @@ -1,17 +1,17 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class Xrb28ProtocolEncoderTest extends ProtocolTest { @Test - public void testEncodePositionPeriodic() { + public void testEncodePositionPeriodic() throws Exception { - var encoder = new Xrb28ProtocolEncoder(null); + var encoder = inject(new Xrb28ProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); @@ -23,9 +23,9 @@ public class Xrb28ProtocolEncoderTest extends ProtocolTest { } @Test - public void testEncodeCustom() { + public void testEncodeCustom() throws Exception { - var encoder = new Xrb28ProtocolEncoder(null); + var encoder = inject(new Xrb28ProtocolEncoder(null)); Command command = new Command(); command.setDeviceId(1); diff --git a/src/test/java/org/traccar/protocol/Xt013ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Xt013ProtocolDecoderTest.java index f8ddd35c3..43c1f0676 100644 --- a/src/test/java/org/traccar/protocol/Xt013ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Xt013ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Xt013ProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class Xt013ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Xt013ProtocolDecoder(null); + var decoder = inject(new Xt013ProtocolDecoder(null)); verifyPosition(decoder, text( "TK,862950021650364,150131090859,+53.267863,+5.767363,0,38,12,0,F,204,08,C94,336C,24,,4.09,1,,,,,,,,"), diff --git a/src/test/java/org/traccar/protocol/Xt2400ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Xt2400ProtocolDecoderTest.java index 1b3f6fcbb..881ff1ee9 100644 --- a/src/test/java/org/traccar/protocol/Xt2400ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Xt2400ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Xt2400ProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class Xt2400ProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new Xt2400ProtocolDecoder(null); + var decoder = inject(new Xt2400ProtocolDecoder(null)); decoder.setConfig("\n:wycfg pcr[1] 012801030405060708090a1213c8545657585a656e7d2cd055595d5e71797a7b7c7e7f80818285866b\n"); diff --git a/src/test/java/org/traccar/protocol/YwtProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/YwtProtocolDecoderTest.java index 1d29b1282..a5a2a11d1 100644 --- a/src/test/java/org/traccar/protocol/YwtProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/YwtProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class YwtProtocolDecoderTest extends ProtocolTest { @@ -8,7 +8,7 @@ public class YwtProtocolDecoderTest extends ProtocolTest { @Test public void testDecode() throws Exception { - var decoder = new YwtProtocolDecoder(null); + var decoder = inject(new YwtProtocolDecoder(null)); verifyPosition(decoder, text( "%RP,1222102985:1,170509033842,E102.146563,N14.582175,,0,320,10,0,00-00-00-00-00-00-00-00-00-00-00-00,,1db2-02b3-52004,3>941.523-32,7>1,19>-16,20>30.9V")); diff --git a/src/test/java/org/traccar/reports/ReportUtilsTest.java b/src/test/java/org/traccar/reports/ReportUtilsTest.java index deb17ddd2..0f14577fd 100644 --- a/src/test/java/org/traccar/reports/ReportUtilsTest.java +++ b/src/test/java/org/traccar/reports/ReportUtilsTest.java @@ -1,29 +1,49 @@ package org.traccar.reports; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import org.apache.velocity.app.VelocityEngine; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.traccar.BaseTest; +import org.traccar.api.security.PermissionsService; +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.helper.model.PositionUtil; +import org.traccar.model.Device; +import org.traccar.model.Position; +import org.traccar.reports.common.ReportUtils; +import org.traccar.reports.model.StopReportItem; +import org.traccar.reports.model.TripReportItem; +import org.traccar.storage.Storage; +import org.traccar.storage.StorageException; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Arrays; -import java.util.Collection; import java.util.Date; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.TimeZone; -import org.junit.Test; -import org.traccar.BaseTest; -import org.traccar.TestIdentityManager; -import org.traccar.model.Position; -import org.traccar.reports.model.StopReport; -import org.traccar.reports.model.TripReport; -import org.traccar.reports.model.TripsConfig; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class ReportUtilsTest extends BaseTest { + + private Storage storage; + + @BeforeEach + public void init() throws StorageException { + storage = mock(Storage.class); + when(storage.getObject(eq(Device.class), any())).thenReturn(mock(Device.class)); + } private Date date(String time) throws ParseException { DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); @@ -38,35 +58,51 @@ public class ReportUtilsTest extends BaseTest { position.setTime(date(time)); position.setValid(true); position.setSpeed(speed); + position.set(Position.KEY_MOTION, speed > 0); position.set(Position.KEY_TOTAL_DISTANCE, totalDistance); return position; } + private Device mockDevice( + double minimalTripDistance, long minimalTripDuration, long minimalParkingDuration, + long minimalNoDataDuration, boolean useIgnition) { + Device device = mock(Device.class); + when(device.getAttributes()).thenReturn(Map.of( + Keys.REPORT_TRIP_MINIMAL_TRIP_DISTANCE.getKey(), minimalTripDistance, + Keys.REPORT_TRIP_MINIMAL_TRIP_DURATION.getKey(), minimalTripDuration, + Keys.REPORT_TRIP_MINIMAL_PARKING_DURATION.getKey(), minimalParkingDuration, + Keys.REPORT_TRIP_MINIMAL_NO_DATA_DURATION.getKey(), minimalNoDataDuration, + Keys.REPORT_TRIP_USE_IGNITION.getKey(), useIgnition)); + return device; + } + @Test public void testCalculateDistance() { Position startPosition = new Position(); startPosition.set(Position.KEY_TOTAL_DISTANCE, 500.0); Position endPosition = new Position(); endPosition.set(Position.KEY_TOTAL_DISTANCE, 700.0); - assertEquals(ReportUtils.calculateDistance(startPosition, endPosition), 200.0, 10); + assertEquals(PositionUtil.calculateDistance(startPosition, endPosition, true), 200.0, 10); startPosition.set(Position.KEY_ODOMETER, 50000); endPosition.set(Position.KEY_ODOMETER, 51000); - assertEquals(ReportUtils.calculateDistance(startPosition, endPosition), 1000.0, 10); + assertEquals(PositionUtil.calculateDistance(startPosition, endPosition, true), 1000.0, 10); } @Test public void testCalculateSpentFuel() { + ReportUtils reportUtils = new ReportUtils( + mock(Config.class), storage, mock(PermissionsService.class), mock(VelocityEngine.class), null); Position startPosition = new Position(); Position endPosition = new Position(); - assertEquals(ReportUtils.calculateFuel(startPosition, endPosition), 0.0, 0.01); + assertEquals(reportUtils.calculateFuel(startPosition, endPosition), 0.0, 0.01); startPosition.set(Position.KEY_FUEL_LEVEL, 0.7); endPosition.set(Position.KEY_FUEL_LEVEL, 0.5); - assertEquals(ReportUtils.calculateFuel(startPosition, endPosition), 0.2, 0.01); + assertEquals(reportUtils.calculateFuel(startPosition, endPosition), 0.2, 0.01); } @Test - public void testDetectTripsSimple() throws ParseException { + public void testDetectTripsSimple() throws Exception { List<Position> data = Arrays.asList( position("2016-01-01 00:00:00.000", 0, 0), @@ -75,18 +111,20 @@ public class ReportUtilsTest extends BaseTest { position("2016-01-01 00:03:00.000", 10, 1000), position("2016-01-01 00:04:00.000", 10, 2000), position("2016-01-01 00:05:00.000", 0, 3000), - position("2016-01-01 00:06:00.000", 0, 3000), - position("2016-01-01 00:07:00.000", 0, 3000)); + position("2016-01-01 00:15:00.000", 0, 3000), + position("2016-01-01 00:25:00.000", 0, 3000)); + when(storage.getObjects(eq(Position.class), any())).thenReturn(data); - TripsConfig tripsConfig = new TripsConfig(500, 300000, 180000, 900000, false, false, 0.01); + Device device = mockDevice(500, 300, 180, 900, false); + ReportUtils reportUtils = new ReportUtils( + mock(Config.class), storage, mock(PermissionsService.class), mock(VelocityEngine.class), null); - Collection<TripReport> trips = ReportUtils.detectTripsAndStops( - new TestIdentityManager(), null, data, tripsConfig, false, TripReport.class); + var trips = reportUtils.slowTripsAndStops(device, new Date(), new Date(), TripReportItem.class); assertNotNull(trips); assertFalse(trips.isEmpty()); - TripReport itemTrip = trips.iterator().next(); + TripReportItem itemTrip = trips.iterator().next(); assertEquals(date("2016-01-01 00:02:00.000"), itemTrip.getStartTime()); assertEquals(date("2016-01-01 00:05:00.000"), itemTrip.getEndTime()); @@ -95,15 +133,14 @@ public class ReportUtilsTest extends BaseTest { assertEquals(10, itemTrip.getMaxSpeed(), 0.01); assertEquals(3000, itemTrip.getDistance(), 0.01); - Collection<StopReport> stops = ReportUtils.detectTripsAndStops( - new TestIdentityManager(), null, data, tripsConfig, false, StopReport.class); + var stops = reportUtils.slowTripsAndStops(device, new Date(), new Date(), StopReportItem.class); assertNotNull(stops); assertFalse(stops.isEmpty()); - Iterator<StopReport> iterator = stops.iterator(); + Iterator<StopReportItem> iterator = stops.iterator(); - StopReport itemStop = iterator.next(); + StopReportItem itemStop = iterator.next(); assertEquals(date("2016-01-01 00:00:00.000"), itemStop.getStartTime()); assertEquals(date("2016-01-01 00:02:00.000"), itemStop.getEndTime()); @@ -112,13 +149,13 @@ public class ReportUtilsTest extends BaseTest { itemStop = iterator.next(); assertEquals(date("2016-01-01 00:05:00.000"), itemStop.getStartTime()); - assertEquals(date("2016-01-01 00:07:00.000"), itemStop.getEndTime()); - assertEquals(120000, itemStop.getDuration()); + assertEquals(date("2016-01-01 00:25:00.000"), itemStop.getEndTime()); + assertEquals(1200000, itemStop.getDuration()); } @Test - public void testDetectTripsSimpleWithIgnition() throws ParseException { + public void testDetectTripsSimpleWithIgnition() throws Exception { List<Position> data = Arrays.asList( position("2016-01-01 00:00:00.000", 0, 0), @@ -127,20 +164,22 @@ public class ReportUtilsTest extends BaseTest { position("2016-01-01 00:03:00.000", 10, 1000), position("2016-01-01 00:04:00.000", 10, 2000), position("2016-01-01 00:05:00.000", 0, 3000), - position("2016-01-01 00:06:00.000", 0, 3000), - position("2016-01-01 00:07:00.000", 0, 3000)); + position("2016-01-01 00:15:00.000", 0, 3000), + position("2016-01-01 00:25:00.000", 0, 3000)); + when(storage.getObjects(eq(Position.class), any())).thenReturn(data); data.get(5).set(Position.KEY_IGNITION, false); - TripsConfig tripsConfig = new TripsConfig(500, 300000, 180000, 900000, true, false, 0.01); + Device device = mockDevice(500, 300, 180, 900, true); + ReportUtils reportUtils = new ReportUtils( + mock(Config.class), storage, mock(PermissionsService.class), mock(VelocityEngine.class), null); - Collection<TripReport> trips = ReportUtils.detectTripsAndStops( - new TestIdentityManager(), null, data, tripsConfig, false, TripReport.class); + var trips = reportUtils.slowTripsAndStops(device, new Date(), new Date(), TripReportItem.class); assertNotNull(trips); assertFalse(trips.isEmpty()); - TripReport itemTrip = trips.iterator().next(); + TripReportItem itemTrip = trips.iterator().next(); assertEquals(date("2016-01-01 00:02:00.000"), itemTrip.getStartTime()); assertEquals(date("2016-01-01 00:05:00.000"), itemTrip.getEndTime()); @@ -149,8 +188,7 @@ public class ReportUtilsTest extends BaseTest { assertEquals(10, itemTrip.getMaxSpeed(), 0.01); assertEquals(3000, itemTrip.getDistance(), 0.01); - trips = ReportUtils.detectTripsAndStops( - new TestIdentityManager(), null, data, tripsConfig, false, TripReport.class); + trips = reportUtils.slowTripsAndStops(device, new Date(), new Date(), TripReportItem.class); assertNotNull(trips); assertFalse(trips.isEmpty()); @@ -164,15 +202,14 @@ public class ReportUtilsTest extends BaseTest { assertEquals(10, itemTrip.getMaxSpeed(), 0.01); assertEquals(3000, itemTrip.getDistance(), 0.01); - Collection<StopReport> stops = ReportUtils.detectTripsAndStops( - new TestIdentityManager(), null, data, tripsConfig, false, StopReport.class); + var stops = reportUtils.slowTripsAndStops(device, new Date(), new Date(), StopReportItem.class); assertNotNull(stops); assertFalse(stops.isEmpty()); - Iterator<StopReport> iterator = stops.iterator(); + Iterator<StopReportItem> iterator = stops.iterator(); - StopReport itemStop = iterator.next(); + StopReportItem itemStop = iterator.next(); assertEquals(date("2016-01-01 00:00:00.000"), itemStop.getStartTime()); assertEquals(date("2016-01-01 00:02:00.000"), itemStop.getEndTime()); @@ -181,13 +218,13 @@ public class ReportUtilsTest extends BaseTest { itemStop = iterator.next(); assertEquals(date("2016-01-01 00:05:00.000"), itemStop.getStartTime()); - assertEquals(date("2016-01-01 00:07:00.000"), itemStop.getEndTime()); - assertEquals(120000, itemStop.getDuration()); + assertEquals(date("2016-01-01 00:25:00.000"), itemStop.getEndTime()); + assertEquals(1200000, itemStop.getDuration()); } @Test - public void testDetectTripsWithFluctuation() throws ParseException { + public void testDetectTripsWithFluctuation() throws Exception { List<Position> data = Arrays.asList( position("2016-01-01 00:00:00.000", 0, 0), @@ -200,18 +237,20 @@ public class ReportUtilsTest extends BaseTest { position("2016-01-01 00:07:00.000", 0, 5000), position("2016-01-01 00:08:00.000", 10, 6000), position("2016-01-01 00:09:00.000", 0, 7000), - position("2016-01-01 00:10:00.000", 0, 7000), - position("2016-01-01 00:11:00.000", 0, 7000)); + position("2016-01-01 00:19:00.000", 0, 7000), + position("2016-01-01 00:29:00.000", 0, 7000)); + when(storage.getObjects(eq(Position.class), any())).thenReturn(data); - TripsConfig tripsConfig = new TripsConfig(500, 300000, 180000, 900000, false, false, 0.01); + Device device = mockDevice(500, 300, 180, 900, false); + ReportUtils reportUtils = new ReportUtils( + mock(Config.class), storage, mock(PermissionsService.class), mock(VelocityEngine.class), null); - Collection<TripReport> trips = ReportUtils.detectTripsAndStops( - new TestIdentityManager(), null, data, tripsConfig, false, TripReport.class); + var trips = reportUtils.slowTripsAndStops(device, new Date(), new Date(), TripReportItem.class); assertNotNull(trips); assertFalse(trips.isEmpty()); - TripReport itemTrip = trips.iterator().next(); + TripReportItem itemTrip = trips.iterator().next(); assertEquals(date("2016-01-01 00:02:00.000"), itemTrip.getStartTime()); assertEquals(date("2016-01-01 00:09:00.000"), itemTrip.getEndTime()); @@ -220,15 +259,14 @@ public class ReportUtilsTest extends BaseTest { assertEquals(10, itemTrip.getMaxSpeed(), 0.01); assertEquals(7000, itemTrip.getDistance(), 0.01); - Collection<StopReport> stops = ReportUtils.detectTripsAndStops( - new TestIdentityManager(), null, data, tripsConfig, false, StopReport.class); + var stops = reportUtils.slowTripsAndStops(device, new Date(), new Date(), StopReportItem.class); assertNotNull(stops); assertFalse(stops.isEmpty()); - Iterator<StopReport> iterator = stops.iterator(); + Iterator<StopReportItem> iterator = stops.iterator(); - StopReport itemStop = iterator.next(); + StopReportItem itemStop = iterator.next(); assertEquals(date("2016-01-01 00:00:00.000"), itemStop.getStartTime()); assertEquals(date("2016-01-01 00:02:00.000"), itemStop.getEndTime()); @@ -237,31 +275,33 @@ public class ReportUtilsTest extends BaseTest { itemStop = iterator.next(); assertEquals(date("2016-01-01 00:09:00.000"), itemStop.getStartTime()); - assertEquals(date("2016-01-01 00:11:00.000"), itemStop.getEndTime()); - assertEquals(120000, itemStop.getDuration()); + assertEquals(date("2016-01-01 00:29:00.000"), itemStop.getEndTime()); + assertEquals(1200000, itemStop.getDuration()); } @Test - public void testDetectStopsOnly() throws ParseException { + public void testDetectStopsOnly() throws Exception { - Collection<Position> data = Arrays.asList( + var data = Arrays.asList( position("2016-01-01 00:00:00.000", 0, 0), position("2016-01-01 00:01:00.000", 0, 0), position("2016-01-01 00:02:00.000", 1, 0), position("2016-01-01 00:03:00.000", 0, 0), position("2016-01-01 00:04:00.000", 1, 0), position("2016-01-01 00:05:00.000", 0, 0)); + when(storage.getObjects(eq(Position.class), any())).thenReturn(data); - TripsConfig tripsConfig = new TripsConfig(500, 300000, 200000, 900000, false, false, 0.01); + Device device = mockDevice(500, 300, 200, 900, false); + ReportUtils reportUtils = new ReportUtils( + mock(Config.class), storage, mock(PermissionsService.class), mock(VelocityEngine.class), null); - Collection<StopReport> result = ReportUtils.detectTripsAndStops( - new TestIdentityManager(), null, data, tripsConfig, false, StopReport.class); + var result = reportUtils.slowTripsAndStops(device, new Date(), new Date(), StopReportItem.class); assertNotNull(result); assertFalse(result.isEmpty()); - StopReport itemStop = result.iterator().next(); + StopReportItem itemStop = result.iterator().next(); assertEquals(date("2016-01-01 00:00:00.000"), itemStop.getStartTime()); assertEquals(date("2016-01-01 00:05:00.000"), itemStop.getEndTime()); @@ -270,25 +310,27 @@ public class ReportUtilsTest extends BaseTest { } @Test - public void testDetectStopsWithTripCut() throws ParseException { + public void testDetectStopsWithTripCut() throws Exception { - Collection<Position> data = Arrays.asList( + var data = Arrays.asList( position("2016-01-01 00:00:00.000", 0, 0), position("2016-01-01 00:01:00.000", 0, 0), position("2016-01-01 00:02:00.000", 0, 0), position("2016-01-01 00:03:00.000", 0, 0), position("2016-01-01 00:04:00.000", 1, 0), position("2016-01-01 00:05:00.000", 2, 0)); + when(storage.getObjects(eq(Position.class), any())).thenReturn(data); - TripsConfig tripsConfig = new TripsConfig(500, 300000, 200000, 900000, false, false, 0.01); + Device device = mockDevice(500, 300, 200, 900, false); + ReportUtils reportUtils = new ReportUtils( + mock(Config.class), storage, mock(PermissionsService.class), mock(VelocityEngine.class), null); - Collection<StopReport> result = ReportUtils.detectTripsAndStops( - new TestIdentityManager(), null, data, tripsConfig, false, StopReport.class); + var result = reportUtils.slowTripsAndStops(device, new Date(), new Date(), StopReportItem.class); assertNotNull(result); assertFalse(result.isEmpty()); - StopReport itemStop = result.iterator().next(); + StopReportItem itemStop = result.iterator().next(); assertEquals(date("2016-01-01 00:00:00.000"), itemStop.getStartTime()); assertEquals(date("2016-01-01 00:04:00.000"), itemStop.getEndTime()); @@ -297,47 +339,51 @@ public class ReportUtilsTest extends BaseTest { } @Test - public void testDetectStopsStartedFromTrip() throws ParseException { + public void testDetectStopsStartedFromTrip() throws Exception { - Collection<Position> data = Arrays.asList( + var data = Arrays.asList( position("2016-01-01 00:00:00.000", 2, 0), position("2016-01-01 00:01:00.000", 1, 0), position("2016-01-01 00:02:00.000", 0, 0), - position("2016-01-01 00:03:00.000", 0, 0), - position("2016-01-01 00:04:00.000", 0, 0), - position("2016-01-01 00:05:00.000", 0, 0)); + position("2016-01-01 00:12:00.000", 0, 0), + position("2016-01-01 00:22:00.000", 0, 0), + position("2016-01-01 00:32:00.000", 0, 0)); + when(storage.getObjects(eq(Position.class), any())).thenReturn(data); - TripsConfig tripsConfig = new TripsConfig(500, 300000, 200000, 900000, false, false, 0.01); + Device device = mockDevice(500, 300, 200, 900, false); + ReportUtils reportUtils = new ReportUtils( + mock(Config.class), storage, mock(PermissionsService.class), mock(VelocityEngine.class), null); - Collection<StopReport> result = ReportUtils.detectTripsAndStops( - new TestIdentityManager(), null, data, tripsConfig, false, StopReport.class); + var result = reportUtils.slowTripsAndStops(device, new Date(), new Date(), StopReportItem.class); assertNotNull(result); assertFalse(result.isEmpty()); - StopReport itemStop = result.iterator().next(); + StopReportItem itemStop = result.iterator().next(); assertEquals(date("2016-01-01 00:02:00.000"), itemStop.getStartTime()); - assertEquals(date("2016-01-01 00:05:00.000"), itemStop.getEndTime()); - assertEquals(180000, itemStop.getDuration()); + assertEquals(date("2016-01-01 00:32:00.000"), itemStop.getEndTime()); + assertEquals(1800000, itemStop.getDuration()); } @Test - public void testDetectStopsMoving() throws ParseException { + public void testDetectStopsMoving() throws Exception { - Collection<Position> data = Arrays.asList( + var data = Arrays.asList( position("2016-01-01 00:00:00.000", 5, 0), position("2016-01-01 00:01:00.000", 5, 0), position("2016-01-01 00:02:00.000", 3, 0), position("2016-01-01 00:03:00.000", 5, 0), position("2016-01-01 00:04:00.000", 5, 0), position("2016-01-01 00:05:00.000", 5, 0)); + when(storage.getObjects(eq(Position.class), any())).thenReturn(data); - TripsConfig tripsConfig = new TripsConfig(500, 300000, 200000, 900000, false, false, 0.01); + Device device = mockDevice(500, 300, 200, 900, false); + ReportUtils reportUtils = new ReportUtils( + mock(Config.class), storage, mock(PermissionsService.class), mock(VelocityEngine.class), null); - Collection<StopReport> result = ReportUtils.detectTripsAndStops( - new TestIdentityManager(), null, data, tripsConfig, false, StopReport.class); + var result = reportUtils.slowTripsAndStops(device, new Date(), new Date(), StopReportItem.class); assertNotNull(result); assertTrue(result.isEmpty()); @@ -345,9 +391,9 @@ public class ReportUtilsTest extends BaseTest { } @Test - public void testDetectTripAndStopByGap() throws ParseException { + public void testDetectTripAndStopByGap() throws Exception { - Collection<Position> data = Arrays.asList( + var data = Arrays.asList( position("2016-01-01 00:00:00.000", 7, 100), position("2016-01-01 00:01:00.000", 7, 300), position("2016-01-01 00:02:00.000", 5, 500), @@ -356,16 +402,18 @@ public class ReportUtilsTest extends BaseTest { position("2016-01-01 00:23:00.000", 2, 700), position("2016-01-01 00:24:00.000", 5, 800), position("2016-01-01 00:25:00.000", 5, 900)); + when(storage.getObjects(eq(Position.class), any())).thenReturn(data); - TripsConfig tripsConfig = new TripsConfig(500, 200000, 200000, 900000, false, false, 0.01); + Device device = mockDevice(500, 200, 200, 900, false); + ReportUtils reportUtils = new ReportUtils( + mock(Config.class), storage, mock(PermissionsService.class), mock(VelocityEngine.class), null); - Collection<TripReport> trips = ReportUtils.detectTripsAndStops( - new TestIdentityManager(), null, data, tripsConfig, false, TripReport.class); + var trips = reportUtils.slowTripsAndStops(device, new Date(), new Date(), TripReportItem.class); assertNotNull(trips); assertFalse(trips.isEmpty()); - TripReport itemTrip = trips.iterator().next(); + TripReportItem itemTrip = trips.iterator().next(); assertEquals(date("2016-01-01 00:00:00.000"), itemTrip.getStartTime()); assertEquals(date("2016-01-01 00:04:00.000"), itemTrip.getEndTime()); @@ -374,13 +422,12 @@ public class ReportUtilsTest extends BaseTest { assertEquals(7, itemTrip.getMaxSpeed(), 0.01); assertEquals(600, itemTrip.getDistance(), 0.01); - Collection<StopReport> stops = ReportUtils.detectTripsAndStops( - new TestIdentityManager(), null, data, tripsConfig, false, StopReport.class); + var stops = reportUtils.slowTripsAndStops(device, new Date(), new Date(), StopReportItem.class); assertNotNull(stops); assertFalse(stops.isEmpty()); - StopReport itemStop = stops.iterator().next(); + StopReportItem itemStop = stops.iterator().next(); assertEquals(date("2016-01-01 00:04:00.000"), itemStop.getStartTime()); assertEquals(date("2016-01-01 00:24:00.000"), itemStop.getEndTime()); diff --git a/src/test/java/org/traccar/speedlimit/OverpassSpeedLimitProviderTest.java b/src/test/java/org/traccar/speedlimit/OverpassSpeedLimitProviderTest.java index dcac78f80..a59d1ce91 100644 --- a/src/test/java/org/traccar/speedlimit/OverpassSpeedLimitProviderTest.java +++ b/src/test/java/org/traccar/speedlimit/OverpassSpeedLimitProviderTest.java @@ -1,21 +1,22 @@ package org.traccar.speedlimit; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; public class OverpassSpeedLimitProviderTest { - @Ignore - @Test - public void test() throws Exception { - testLocationProvider(); - } + private final Client client = ClientBuilder.newClient(); - public void testLocationProvider() throws Exception { - SpeedLimitProvider provider = new OverpassSpeedLimitProvider("http://8.8.8.8/api/interpreter"); + @Disabled + @Test + public void testOverpass() throws Exception { + SpeedLimitProvider provider = new OverpassSpeedLimitProvider(client, "http://8.8.8.8/api/interpreter"); provider.getSpeedLimit(34.74767, -82.48098, new SpeedLimitProvider.SpeedLimitProviderCallback() { @Override diff --git a/src/test/java/org/traccar/web/WebServerTest.java b/src/test/java/org/traccar/web/WebServerTest.java index ba4124e44..694dab18a 100644 --- a/src/test/java/org/traccar/web/WebServerTest.java +++ b/src/test/java/org/traccar/web/WebServerTest.java @@ -1,6 +1,6 @@ package org.traccar.web; -import org.junit.Test; +import org.junit.jupiter.api.Test; import javax.naming.Context; import javax.naming.InitialContext; |