aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/CONTRIBUTING.md2
-rw-r--r--.github/workflows/release.yml14
-rw-r--r--.gitignore2
-rw-r--r--build.gradle108
-rw-r--r--gradle/checkstyle.xml2
-rw-r--r--schema/changelog-5.10.xml17
-rw-r--r--schema/changelog-5.11.xml17
-rw-r--r--schema/changelog-5.7.xml25
-rw-r--r--schema/changelog-5.8.xml18
-rw-r--r--schema/changelog-5.9.xml27
-rw-r--r--schema/changelog-master.xml5
-rw-r--r--setup/cloud-init.yaml29
-rw-r--r--setup/default.xml13
-rwxr-xr-xsetup/package.sh29
-rw-r--r--setup/traccar.iss2
-rw-r--r--src/main/java/org/traccar/BaseMqttProtocolDecoder.java96
-rw-r--r--src/main/java/org/traccar/BasePipelineFactory.java31
-rw-r--r--src/main/java/org/traccar/BaseProtocol.java7
-rw-r--r--src/main/java/org/traccar/BaseProtocolDecoder.java22
-rw-r--r--src/main/java/org/traccar/BaseProtocolEncoder.java52
-rw-r--r--src/main/java/org/traccar/ExtendedObjectDecoder.java14
-rw-r--r--src/main/java/org/traccar/Main.java32
-rw-r--r--src/main/java/org/traccar/MainEventHandler.java7
-rw-r--r--src/main/java/org/traccar/MainModule.java91
-rw-r--r--src/main/java/org/traccar/PositionForwardingHandler.java6
-rw-r--r--src/main/java/org/traccar/ServerManager.java4
-rw-r--r--src/main/java/org/traccar/WindowsService.java4
-rw-r--r--src/main/java/org/traccar/api/AsyncSocket.java40
-rw-r--r--src/main/java/org/traccar/api/AsyncSocketServlet.java6
-rw-r--r--src/main/java/org/traccar/api/BaseObjectResource.java39
-rw-r--r--src/main/java/org/traccar/api/BaseResource.java6
-rw-r--r--src/main/java/org/traccar/api/CorsResponseFilter.java10
-rw-r--r--src/main/java/org/traccar/api/DateParameterConverterProvider.java4
-rw-r--r--src/main/java/org/traccar/api/ExtendedObjectResource.java4
-rw-r--r--src/main/java/org/traccar/api/MediaFilter.java31
-rw-r--r--src/main/java/org/traccar/api/ResourceErrorHandler.java6
-rw-r--r--src/main/java/org/traccar/api/SimpleObjectResource.java4
-rw-r--r--src/main/java/org/traccar/api/resource/AttributeResource.java28
-rw-r--r--src/main/java/org/traccar/api/resource/CalendarResource.java8
-rw-r--r--src/main/java/org/traccar/api/resource/CommandResource.java48
-rw-r--r--src/main/java/org/traccar/api/resource/DeviceResource.java89
-rw-r--r--src/main/java/org/traccar/api/resource/DriverResource.java8
-rw-r--r--src/main/java/org/traccar/api/resource/EventResource.java16
-rw-r--r--src/main/java/org/traccar/api/resource/GeofenceResource.java8
-rw-r--r--src/main/java/org/traccar/api/resource/GroupResource.java8
-rw-r--r--src/main/java/org/traccar/api/resource/MaintenanceResource.java8
-rw-r--r--src/main/java/org/traccar/api/resource/NotificationResource.java56
-rw-r--r--src/main/java/org/traccar/api/resource/OrderResource.java8
-rw-r--r--src/main/java/org/traccar/api/resource/PasswordResource.java24
-rw-r--r--src/main/java/org/traccar/api/resource/PermissionsResource.java34
-rw-r--r--src/main/java/org/traccar/api/resource/PositionResource.java39
-rw-r--r--src/main/java/org/traccar/api/resource/ReportResource.java69
-rw-r--r--src/main/java/org/traccar/api/resource/ServerResource.java101
-rw-r--r--src/main/java/org/traccar/api/resource/SessionResource.java138
-rw-r--r--src/main/java/org/traccar/api/resource/StatisticsResource.java12
-rw-r--r--src/main/java/org/traccar/api/resource/UserResource.java57
-rw-r--r--src/main/java/org/traccar/api/security/CodeRequiredException.java (renamed from src/main/java/org/traccar/protocol/NdtpV6FrameDecoder.java)20
-rw-r--r--src/main/java/org/traccar/api/security/LoginResult.java29
-rw-r--r--src/main/java/org/traccar/api/security/LoginService.java66
-rw-r--r--src/main/java/org/traccar/api/security/PermissionsService.java30
-rw-r--r--src/main/java/org/traccar/api/security/SecurityRequestFilter.java66
-rw-r--r--src/main/java/org/traccar/api/security/UserPrincipal.java11
-rw-r--r--src/main/java/org/traccar/api/security/UserSecurityContext.java2
-rw-r--r--src/main/java/org/traccar/api/signature/CryptoManager.java4
-rw-r--r--src/main/java/org/traccar/api/signature/TokenManager.java24
-rw-r--r--src/main/java/org/traccar/broadcast/BaseBroadcastService.java128
-rw-r--r--src/main/java/org/traccar/broadcast/BroadcastInterface.java13
-rw-r--r--src/main/java/org/traccar/broadcast/BroadcastMessage.java114
-rw-r--r--src/main/java/org/traccar/broadcast/MulticastBroadcastService.java94
-rw-r--r--src/main/java/org/traccar/broadcast/RedisBroadcastService.java125
-rw-r--r--src/main/java/org/traccar/config/Config.java4
-rw-r--r--src/main/java/org/traccar/config/Keys.java370
-rw-r--r--src/main/java/org/traccar/database/CommandsManager.java29
-rw-r--r--src/main/java/org/traccar/database/DeviceLookupService.java6
-rw-r--r--src/main/java/org/traccar/database/MediaManager.java4
-rw-r--r--src/main/java/org/traccar/database/NotificationManager.java35
-rw-r--r--src/main/java/org/traccar/database/OpenIdProvider.java204
-rw-r--r--src/main/java/org/traccar/database/StatisticsManager.java29
-rw-r--r--src/main/java/org/traccar/forward/AmqpClient.java58
-rw-r--r--src/main/java/org/traccar/forward/EventForwarderAmqp.java48
-rw-r--r--src/main/java/org/traccar/forward/EventForwarderJson.java8
-rw-r--r--src/main/java/org/traccar/forward/EventForwarderMqtt.java7
-rw-r--r--src/main/java/org/traccar/forward/NetworkForwarder.java77
-rw-r--r--src/main/java/org/traccar/forward/PositionForwarderAmqp.java48
-rw-r--r--src/main/java/org/traccar/forward/PositionForwarderJson.java12
-rw-r--r--src/main/java/org/traccar/forward/PositionForwarderRedis.java50
-rw-r--r--src/main/java/org/traccar/forward/PositionForwarderUrl.java6
-rw-r--r--src/main/java/org/traccar/geocoder/BanGeocoder.java6
-rw-r--r--src/main/java/org/traccar/geocoder/BingMapsGeocoder.java6
-rw-r--r--src/main/java/org/traccar/geocoder/FactualGeocoder.java4
-rw-r--r--src/main/java/org/traccar/geocoder/GeoapifyGeocoder.java6
-rw-r--r--src/main/java/org/traccar/geocoder/GeocodeFarmGeocoder.java4
-rw-r--r--src/main/java/org/traccar/geocoder/GeocodeXyzGeocoder.java4
-rw-r--r--src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java4
-rw-r--r--src/main/java/org/traccar/geocoder/GoogleGeocoder.java8
-rw-r--r--src/main/java/org/traccar/geocoder/HereGeocoder.java62
-rw-r--r--src/main/java/org/traccar/geocoder/JsonGeocoder.java8
-rw-r--r--src/main/java/org/traccar/geocoder/LocationIqGeocoder.java2
-rw-r--r--src/main/java/org/traccar/geocoder/MapQuestGeocoder.java6
-rw-r--r--src/main/java/org/traccar/geocoder/MapTilerGeocoder.java6
-rw-r--r--src/main/java/org/traccar/geocoder/MapboxGeocoder.java8
-rw-r--r--src/main/java/org/traccar/geocoder/MapmyIndiaGeocoder.java6
-rw-r--r--src/main/java/org/traccar/geocoder/NominatimGeocoder.java4
-rw-r--r--src/main/java/org/traccar/geocoder/OpenCageGeocoder.java6
-rw-r--r--src/main/java/org/traccar/geocoder/PositionStackGeocoder.java6
-rw-r--r--src/main/java/org/traccar/geocoder/TomTomGeocoder.java6
-rw-r--r--src/main/java/org/traccar/geolocation/GoogleGeolocationProvider.java2
-rw-r--r--src/main/java/org/traccar/geolocation/MozillaGeolocationProvider.java2
-rw-r--r--src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java6
-rw-r--r--src/main/java/org/traccar/geolocation/UniversalGeolocationProvider.java8
-rw-r--r--src/main/java/org/traccar/geolocation/UnwiredGeolocationProvider.java8
-rw-r--r--src/main/java/org/traccar/handler/AcknowledgementHandler.java121
-rw-r--r--src/main/java/org/traccar/handler/ComputedAttributesHandler.java68
-rw-r--r--src/main/java/org/traccar/handler/CopyAttributesHandler.java4
-rw-r--r--src/main/java/org/traccar/handler/DefaultDataHandler.java4
-rw-r--r--src/main/java/org/traccar/handler/DistanceHandler.java30
-rw-r--r--src/main/java/org/traccar/handler/EngineHoursHandler.java4
-rw-r--r--src/main/java/org/traccar/handler/FilterHandler.java66
-rw-r--r--src/main/java/org/traccar/handler/GeofenceHandler.java52
-rw-r--r--src/main/java/org/traccar/handler/GeolocationHandler.java7
-rw-r--r--src/main/java/org/traccar/handler/HemisphereHandler.java4
-rw-r--r--src/main/java/org/traccar/handler/MotionHandler.java20
-rw-r--r--src/main/java/org/traccar/handler/NetworkForwarderHandler.java72
-rw-r--r--src/main/java/org/traccar/handler/RemoteAddressHandler.java4
-rw-r--r--src/main/java/org/traccar/handler/SpeedLimitHandler.java4
-rw-r--r--src/main/java/org/traccar/handler/StandardLoggingHandler.java65
-rw-r--r--src/main/java/org/traccar/handler/TimeHandler.java4
-rw-r--r--src/main/java/org/traccar/handler/events/AlertEventHandler.java4
-rw-r--r--src/main/java/org/traccar/handler/events/BaseEventHandler.java2
-rw-r--r--src/main/java/org/traccar/handler/events/BehaviorEventHandler.java4
-rw-r--r--src/main/java/org/traccar/handler/events/CommandResultEventHandler.java4
-rw-r--r--src/main/java/org/traccar/handler/events/DriverEventHandler.java4
-rw-r--r--src/main/java/org/traccar/handler/events/FuelEventHandler.java10
-rw-r--r--src/main/java/org/traccar/handler/events/GeofenceEventHandler.java72
-rw-r--r--src/main/java/org/traccar/handler/events/IgnitionEventHandler.java4
-rw-r--r--src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java23
-rw-r--r--src/main/java/org/traccar/handler/events/MediaEventHandler.java4
-rw-r--r--src/main/java/org/traccar/handler/events/MotionEventHandler.java21
-rw-r--r--src/main/java/org/traccar/handler/events/OverspeedEventHandler.java14
-rw-r--r--src/main/java/org/traccar/helper/BufferUtil.java16
-rw-r--r--src/main/java/org/traccar/helper/ObdDecoder.java36
-rw-r--r--src/main/java/org/traccar/helper/ObjectMapperContextResolver.java4
-rw-r--r--src/main/java/org/traccar/helper/Parser.java11
-rw-r--r--src/main/java/org/traccar/helper/WebHelper.java (renamed from src/main/java/org/traccar/helper/ServletHelper.java)26
-rw-r--r--src/main/java/org/traccar/helper/model/AttributeUtil.java106
-rw-r--r--src/main/java/org/traccar/helper/model/DeviceUtil.java48
-rw-r--r--src/main/java/org/traccar/helper/model/UserUtil.java10
-rw-r--r--src/main/java/org/traccar/mail/LogMailManager.java18
-rw-r--r--src/main/java/org/traccar/mail/MailManager.java12
-rw-r--r--src/main/java/org/traccar/mail/SmtpMailManager.java30
-rw-r--r--src/main/java/org/traccar/model/Calendar.java13
-rw-r--r--src/main/java/org/traccar/model/CellTower.java2
-rw-r--r--src/main/java/org/traccar/model/Device.java44
-rw-r--r--src/main/java/org/traccar/model/Driver.java2
-rw-r--r--src/main/java/org/traccar/model/Event.java1
-rw-r--r--src/main/java/org/traccar/model/ExtendedModel.java11
-rw-r--r--src/main/java/org/traccar/model/Geofence.java16
-rw-r--r--src/main/java/org/traccar/model/LogRecord.java79
-rw-r--r--src/main/java/org/traccar/model/Notification.java26
-rw-r--r--src/main/java/org/traccar/model/ObjectOperation.java7
-rw-r--r--src/main/java/org/traccar/model/Position.java23
-rw-r--r--src/main/java/org/traccar/model/Report.java16
-rw-r--r--src/main/java/org/traccar/model/Schedulable.java (renamed from src/main/java/org/traccar/model/ScheduledModel.java)16
-rw-r--r--src/main/java/org/traccar/model/Server.java38
-rw-r--r--src/main/java/org/traccar/model/User.java22
-rw-r--r--src/main/java/org/traccar/model/WifiAccessPoint.java2
-rw-r--r--src/main/java/org/traccar/notification/NotificationFormatter.java13
-rw-r--r--src/main/java/org/traccar/notification/NotificationMessage.java12
-rw-r--r--src/main/java/org/traccar/notification/NotificatorManager.java18
-rw-r--r--src/main/java/org/traccar/notification/TextTemplateFormatter.java20
-rw-r--r--src/main/java/org/traccar/notificators/Notificator.java24
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorCommand.java63
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorFirebase.java87
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorMail.java20
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorNull.java34
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorPushover.java20
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorSms.java18
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorTelegram.java20
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorTraccar.java92
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorWeb.java17
-rw-r--r--src/main/java/org/traccar/protocol/AdmProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/AisProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/AlematicsProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/AnytrekProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/ApelProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/AplicomProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/AppelloProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/AquilaProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Ardi01Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/ArknavProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/ArknavX8Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/ArmoliProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/ArnaviProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/AstraProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/At2000Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/AtrackProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java202
-rw-r--r--src/main/java/org/traccar/protocol/AuroProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/AustinNbProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/AutoFonProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/AutoGradeProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/AutoTrackProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/AvemaProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Avl301Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/B2316Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/B2316ProtocolDecoder.java6
-rw-r--r--src/main/java/org/traccar/protocol/BceProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/BlackKiteProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/BlueProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/BoxProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/BstplProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/C2stekProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/CalAmpProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/CarTrackProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/CarcellProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/CarscopProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/CastelProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/CastelProtocolDecoder.java33
-rw-r--r--src/main/java/org/traccar/protocol/CautelaProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/CellocatorProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/CguardProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/CityeasyProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/ContinentalProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/CradlepointProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/DingtekProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/DishaProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/DmtHttpProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/DmtHttpProtocolDecoder.java6
-rw-r--r--src/main/java/org/traccar/protocol/DmtProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/DolphinProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/DraginoProtocol.java42
-rw-r--r--src/main/java/org/traccar/protocol/DraginoProtocolDecoder.java78
-rw-r--r--src/main/java/org/traccar/protocol/Dsf22Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/DualcamProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/DualcamProtocolDecoder.java59
-rw-r--r--src/main/java/org/traccar/protocol/DwayProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/EasyTrackProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java70
-rw-r--r--src/main/java/org/traccar/protocol/EelinkProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/EgtsProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/EnforaProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/EnnfuProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/EnvotechProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/EsealProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/EskyProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/ExtremTracProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/FifotrackProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java21
-rw-r--r--src/main/java/org/traccar/protocol/FleetGuideProtocol.java36
-rw-r--r--src/main/java/org/traccar/protocol/FleetGuideProtocolDecoder.java328
-rw-r--r--src/main/java/org/traccar/protocol/FlespiProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/FlespiProtocolDecoder.java26
-rw-r--r--src/main/java/org/traccar/protocol/FlexApiProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/FlexApiProtocolDecoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/FlexCommProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/FlexibleReportProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/FlextrackProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/FoxProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/FreedomProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/FreematicsProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/FreematicsProtocolDecoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/FutureWayProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/G1rusProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/GalileoFrameDecoder.java16
-rw-r--r--src/main/java/org/traccar/protocol/GalileoProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/GatorProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/GatorProtocolDecoder.java5
-rw-r--r--src/main/java/org/traccar/protocol/GatorProtocolEncoder.java93
-rw-r--r--src/main/java/org/traccar/protocol/GenxProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Gl100Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Gl200Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Gl200ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java1388
-rw-r--r--src/main/java/org/traccar/protocol/GlobalSatProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/GlobalstarProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java56
-rw-r--r--src/main/java/org/traccar/protocol/GnxProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/GoSafeProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/GoSafeProtocolDecoder.java12
-rw-r--r--src/main/java/org/traccar/protocol/GotopProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Gps056Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Gps103Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/GpsGateProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/GpsMarkerProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/GpsmtaProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/GranitProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Gs100Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Gt02Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Gt06Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java227
-rw-r--r--src/main/java/org/traccar/protocol/Gt06ProtocolEncoder.java7
-rw-r--r--src/main/java/org/traccar/protocol/Gt30Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/H02Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/HaicomProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/HomtecsProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/HoopoProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/HoopoProtocolDecoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/HuaShengProtocol.java12
-rw-r--r--src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java22
-rw-r--r--src/main/java/org/traccar/protocol/HuaShengProtocolEncoder.java85
-rw-r--r--src/main/java/org/traccar/protocol/HuabaoProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java283
-rw-r--r--src/main/java/org/traccar/protocol/HunterProProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/IdplProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/IntellitracProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/IotmProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/IotmProtocolDecoder.java147
-rw-r--r--src/main/java/org/traccar/protocol/ItsProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Ivt401Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/JidoProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/JpKorjarProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Jt600FrameDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Jt600Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java24
-rw-r--r--src/main/java/org/traccar/protocol/KenjiProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/KhdProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/KhdProtocolDecoder.java25
-rw-r--r--src/main/java/org/traccar/protocol/L100Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/LacakProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/LacakProtocolDecoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/LaipacProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java33
-rw-r--r--src/main/java/org/traccar/protocol/LeafSpyProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/M2cProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/M2mProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/MaestroProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/ManPowerProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Mavlink2Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/MegastekProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/MeiligaoProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/MeiligaoProtocolEncoder.java3
-rw-r--r--src/main/java/org/traccar/protocol/MeitrackProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java96
-rw-r--r--src/main/java/org/traccar/protocol/MictrackProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/MilesmateProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/MiniFinderProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/MiniFinderProtocolDecoder.java16
-rw-r--r--src/main/java/org/traccar/protocol/Minifinder2Protocol.java11
-rw-r--r--src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java231
-rw-r--r--src/main/java/org/traccar/protocol/Minifinder2ProtocolEncoder.java72
-rw-r--r--src/main/java/org/traccar/protocol/MobilogixProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/MoovboxProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/MotorProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Mta6Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Mta6ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/MtxProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/MxtProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/NavigilProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/NavisProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/NavisetProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/NavtelecomProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java135
-rw-r--r--src/main/java/org/traccar/protocol/NdtpV6Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/NeosProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/NetProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/NiotProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/NoranProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/NtoProtocol.java42
-rw-r--r--src/main/java/org/traccar/protocol/NtoProtocolDecoder.java92
-rw-r--r--src/main/java/org/traccar/protocol/NvsProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/NyitechProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/ObdDongleProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/OigoProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/OkoProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/OmnicommProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/OpenGtsProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/OrbcommProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/OrbcommProtocolDecoder.java8
-rw-r--r--src/main/java/org/traccar/protocol/OrionProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/OsmAndProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/OutsafeProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/OutsafeProtocolDecoder.java10
-rw-r--r--src/main/java/org/traccar/protocol/OwnTracksProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/PacificTrackProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/PathAwayProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/PiligrimProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/PluginProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/PolteProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/PolteProtocolDecoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/PortmanProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/PortmanProtocolDecoder.java24
-rw-r--r--src/main/java/org/traccar/protocol/PositrexProtocol.java36
-rw-r--r--src/main/java/org/traccar/protocol/PositrexProtocolDecoder.java116
-rw-r--r--src/main/java/org/traccar/protocol/PretraceProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/PricolProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/ProgressProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/PstProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Pt215Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Pt3000Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Pt502Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Pt60Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/PuiProtocol.java40
-rw-r--r--src/main/java/org/traccar/protocol/PuiProtocolDecoder.java73
-rw-r--r--src/main/java/org/traccar/protocol/R12wProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/RaceDynamicsProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/RadarProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/RamacProtocol.java42
-rw-r--r--src/main/java/org/traccar/protocol/RamacProtocolDecoder.java112
-rw-r--r--src/main/java/org/traccar/protocol/RaveonProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/RecodaProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/RetranslatorProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/RfTrackProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/RfTrackProtocolDecoder.java6
-rw-r--r--src/main/java/org/traccar/protocol/RitiProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/RoboTrackProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/RstProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/RstProtocolDecoder.java36
-rw-r--r--src/main/java/org/traccar/protocol/RuptelaProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java167
-rw-r--r--src/main/java/org/traccar/protocol/S168Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/SabertekProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/SanavProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/SanulProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/SatsolProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/SigfoxProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java36
-rw-r--r--src/main/java/org/traccar/protocol/SiwiProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/SkypatrolProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/SmartSoleProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/SmokeyProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/SolarPoweredProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/SpotProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/StarLinkProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/StarcomProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/StarcomProtocolDecoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/StartekFrameDecoder.java49
-rw-r--r--src/main/java/org/traccar/protocol/StartekProtocol.java7
-rw-r--r--src/main/java/org/traccar/protocol/StartekProtocolDecoder.java139
-rw-r--r--src/main/java/org/traccar/protocol/StbProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/StbProtocolDecoder.java6
-rw-r--r--src/main/java/org/traccar/protocol/Stl060Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/SuntechProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java29
-rw-r--r--src/main/java/org/traccar/protocol/SupermateProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/SviasProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/SwiftechProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/T55Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/T55ProtocolDecoder.java51
-rw-r--r--src/main/java/org/traccar/protocol/T57Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/T622IridiumProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/T622IridiumProtocolDecoder.java149
-rw-r--r--src/main/java/org/traccar/protocol/T800xProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/T800xProtocolDecoder.java16
-rw-r--r--src/main/java/org/traccar/protocol/TaipPrefixEncoder.java24
-rw-r--r--src/main/java/org/traccar/protocol/TaipProtocol.java13
-rw-r--r--src/main/java/org/traccar/protocol/TaipProtocolDecoder.java28
-rw-r--r--src/main/java/org/traccar/protocol/TechTltProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/TechtoCruzProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/TekProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/TelemaxProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/TelicProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/TeltonikaFrameDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/TeltonikaProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java120
-rw-r--r--src/main/java/org/traccar/protocol/TeraTrackProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/TeraTrackProtocolDecoder.java6
-rw-r--r--src/main/java/org/traccar/protocol/ThinkPowerProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/ThinkRaceProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/ThurayaProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Tk102Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Tk103Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java107
-rw-r--r--src/main/java/org/traccar/protocol/Tlt2hProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java53
-rw-r--r--src/main/java/org/traccar/protocol/TlvProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/TmgProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/TopflytechProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/TopinProtocol.java11
-rw-r--r--src/main/java/org/traccar/protocol/TopinProtocolDecoder.java8
-rw-r--r--src/main/java/org/traccar/protocol/TotemProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/TotemProtocolDecoder.java175
-rw-r--r--src/main/java/org/traccar/protocol/Tr20Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Tr900Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/TrackboxProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/TrakMateProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/TramigoFrameDecoder.java8
-rw-r--r--src/main/java/org/traccar/protocol/TramigoProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/TramigoProtocolDecoder.java264
-rw-r--r--src/main/java/org/traccar/protocol/TranSyncProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/TranSyncProtocolDecoder.java166
-rw-r--r--src/main/java/org/traccar/protocol/TrvProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/TrvProtocolDecoder.java43
-rw-r--r--src/main/java/org/traccar/protocol/Tt8850Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/TytanProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/TzoneProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java37
-rw-r--r--src/main/java/org/traccar/protocol/UlbotechProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/UproProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/UproProtocolDecoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/UuxProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/V680Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/ValtrackProtocol.java41
-rw-r--r--src/main/java/org/traccar/protocol/ValtrackProtocolDecoder.java84
-rw-r--r--src/main/java/org/traccar/protocol/VisiontekProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/VltProtocol.java43
-rw-r--r--src/main/java/org/traccar/protocol/VltProtocolDecoder.java139
-rw-r--r--src/main/java/org/traccar/protocol/VnetProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Vt200Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Vt200ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/VtfmsProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/WatchFrameDecoder.java23
-rw-r--r--src/main/java/org/traccar/protocol/WatchProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/WatchProtocolDecoder.java7
-rw-r--r--src/main/java/org/traccar/protocol/WialonProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/WialonProtocolDecoder.java21
-rw-r--r--src/main/java/org/traccar/protocol/WliProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/WondexProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/WristbandProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Xexun2Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Xexun2ProtocolDecoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/XexunProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/XirgoProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Xrb28Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java51
-rw-r--r--src/main/java/org/traccar/protocol/Xt013Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Xt2400Protocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/YwtProtocol.java2
-rw-r--r--src/main/java/org/traccar/reports/CombinedReportProvider.java83
-rw-r--r--src/main/java/org/traccar/reports/CsvExportProvider.java2
-rw-r--r--src/main/java/org/traccar/reports/DevicesReportProvider.java78
-rw-r--r--src/main/java/org/traccar/reports/EventsReportProvider.java7
-rw-r--r--src/main/java/org/traccar/reports/GpxExportProvider.java2
-rw-r--r--src/main/java/org/traccar/reports/KmlExportProvider.java2
-rw-r--r--src/main/java/org/traccar/reports/RouteReportProvider.java19
-rw-r--r--src/main/java/org/traccar/reports/StopsReportProvider.java18
-rw-r--r--src/main/java/org/traccar/reports/SummaryReportProvider.java125
-rw-r--r--src/main/java/org/traccar/reports/TripsReportProvider.java18
-rw-r--r--src/main/java/org/traccar/reports/common/ExpressionEvaluatorFactory.java58
-rw-r--r--src/main/java/org/traccar/reports/common/ReportMailer.java12
-rw-r--r--src/main/java/org/traccar/reports/common/ReportUtils.java178
-rw-r--r--src/main/java/org/traccar/reports/common/TripsConfig.java39
-rw-r--r--src/main/java/org/traccar/reports/model/CombinedReportItem.java65
-rw-r--r--src/main/java/org/traccar/reports/model/DeviceReportItem.java48
-rw-r--r--src/main/java/org/traccar/schedule/ScheduleManager.java19
-rw-r--r--src/main/java/org/traccar/schedule/TaskClearStatus.java43
-rw-r--r--src/main/java/org/traccar/schedule/TaskDeleteTemporary.java61
-rw-r--r--src/main/java/org/traccar/schedule/TaskDeviceInactivityCheck.java39
-rw-r--r--src/main/java/org/traccar/schedule/TaskExpirations.java130
-rw-r--r--src/main/java/org/traccar/schedule/TaskHealthCheck.java28
-rw-r--r--src/main/java/org/traccar/schedule/TaskReports.java16
-rw-r--r--src/main/java/org/traccar/schedule/TaskWebSocketKeepalive.java2
-rw-r--r--src/main/java/org/traccar/session/ConnectionManager.java86
-rw-r--r--src/main/java/org/traccar/session/DeviceSession.java9
-rw-r--r--src/main/java/org/traccar/session/Endpoint.java58
-rw-r--r--src/main/java/org/traccar/session/cache/CacheGraph.java139
-rw-r--r--src/main/java/org/traccar/session/cache/CacheKey.java4
-rw-r--r--src/main/java/org/traccar/session/cache/CacheManager.java390
-rw-r--r--src/main/java/org/traccar/session/cache/CacheNode.java40
-rw-r--r--src/main/java/org/traccar/session/cache/CacheValue.java53
-rw-r--r--src/main/java/org/traccar/session/cache/WeakValueMap.java44
-rw-r--r--src/main/java/org/traccar/session/state/OverspeedProcessor.java44
-rw-r--r--src/main/java/org/traccar/sms/HttpSmsClient.java19
-rw-r--r--src/main/java/org/traccar/sms/SmsManager.java3
-rw-r--r--src/main/java/org/traccar/speedlimit/OverpassSpeedLimitProvider.java19
-rw-r--r--src/main/java/org/traccar/storage/DatabaseModule.java2
-rw-r--r--src/main/java/org/traccar/storage/DatabaseStorage.java2
-rw-r--r--src/main/java/org/traccar/storage/MemoryStorage.java126
-rw-r--r--src/main/java/org/traccar/web/ConsoleServlet.java8
-rw-r--r--src/main/java/org/traccar/web/ModernDefaultServlet.java59
-rw-r--r--src/main/java/org/traccar/web/OverrideFilter.java88
-rw-r--r--src/main/java/org/traccar/web/ResponseWrapper.java83
-rw-r--r--src/main/java/org/traccar/web/ThrottlingFilter.java17
-rw-r--r--src/main/java/org/traccar/web/WebInjectionManagerFactory.java2
-rw-r--r--src/main/java/org/traccar/web/WebModule.java3
-rw-r--r--src/main/java/org/traccar/web/WebRequestLog.java57
-rw-r--r--src/main/java/org/traccar/web/WebServer.java35
-rw-r--r--src/main/resources/META-INF/services/org.jxls.expression.ExpressionEvaluatorFactory1
-rw-r--r--src/test/java/org/traccar/BaseTest.java5
-rw-r--r--src/test/java/org/traccar/ProtocolTest.java79
-rw-r--r--src/test/java/org/traccar/calendar/CalendarTest.java19
-rw-r--r--src/test/java/org/traccar/config/ConfigTest.java4
-rw-r--r--src/test/java/org/traccar/database/GroupTreeTest.java4
-rw-r--r--src/test/java/org/traccar/forward/PositionForwarderUrlTest.java4
-rw-r--r--src/test/java/org/traccar/geocoder/AddressFormatTest.java4
-rw-r--r--src/test/java/org/traccar/geocoder/GeocoderTest.java40
-rw-r--r--src/test/java/org/traccar/geofence/GeofenceCircleTest.java8
-rw-r--r--src/test/java/org/traccar/geofence/GeofencePolygonTest.java8
-rw-r--r--src/test/java/org/traccar/geofence/GeofencePolylineTest.java8
-rw-r--r--src/test/java/org/traccar/geolocation/GeolocationProviderTest.java14
-rw-r--r--src/test/java/org/traccar/handler/ComputedAttributesTest.java6
-rw-r--r--src/test/java/org/traccar/handler/DistanceHandlerTest.java4
-rw-r--r--src/test/java/org/traccar/handler/FilterHandlerTest.java31
-rw-r--r--src/test/java/org/traccar/handler/MotionHandlerTest.java20
-rw-r--r--src/test/java/org/traccar/handler/events/AlertEventHandlerTest.java6
-rw-r--r--src/test/java/org/traccar/handler/events/CommandResultEventHandlerTest.java6
-rw-r--r--src/test/java/org/traccar/handler/events/IgnitionEventHandlerTest.java4
-rw-r--r--src/test/java/org/traccar/handler/events/MaintenanceEventHandlerTest.java21
-rw-r--r--src/test/java/org/traccar/handler/events/MotionEventHandlerTest.java12
-rw-r--r--src/test/java/org/traccar/handler/events/OverspeedEventHandlerTest.java18
-rw-r--r--src/test/java/org/traccar/helper/BcdUtilTest.java4
-rw-r--r--src/test/java/org/traccar/helper/BitBufferTest.java4
-rw-r--r--src/test/java/org/traccar/helper/BitUtilTest.java8
-rw-r--r--src/test/java/org/traccar/helper/BufferUtilTest.java4
-rw-r--r--src/test/java/org/traccar/helper/ChecksumTest.java4
-rw-r--r--src/test/java/org/traccar/helper/DateBuilderTest.java4
-rw-r--r--src/test/java/org/traccar/helper/DateUtilTest.java4
-rw-r--r--src/test/java/org/traccar/helper/DistanceCalculatorTest.java4
-rw-r--r--src/test/java/org/traccar/helper/LogTest.java4
-rw-r--r--src/test/java/org/traccar/helper/ObdDecoderTest.java4
-rw-r--r--src/test/java/org/traccar/helper/PatternBuilderTest.java4
-rw-r--r--src/test/java/org/traccar/helper/PatternUtilTest.java8
-rw-r--r--src/test/java/org/traccar/helper/ServletHelperTest.java65
-rw-r--r--src/test/java/org/traccar/helper/WebHelperTest.java39
-rw-r--r--src/test/java/org/traccar/notification/NotificiationMailTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/AdmFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/AdmProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/AdmProtocolEncoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/AisProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/AlematicsProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/AnytrekProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/ApelProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/AplicomFrameDecoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/AplicomProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/AppelloProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/AquilaProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Ardi01ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/ArknavProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/ArknavX8ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/ArmoliProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/ArnaviBinaryProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/ArnaviFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/ArnaviTextProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/AstraProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/At2000FrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/At2000ProtocolDecoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/AtrackFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/AtrackProtocolDecoderTest.java8
-rw-r--r--src/test/java/org/traccar/protocol/AuroProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/AustinNbProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/AutoFonProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/AutoGradeProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/AutoTrackProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/AvemaProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Avl301ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/B2316ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/BceFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/BceProtocolDecoderTest.java6
-rw-r--r--src/test/java/org/traccar/protocol/BceProtocolEncoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/BlackKiteProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/BlueProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/BoxProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/BstplProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/C2stekProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/CalAmpProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/CarTrackProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/CarscopProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/CastelProtocolDecoderTest.java10
-rw-r--r--src/test/java/org/traccar/protocol/CastelProtocolEncoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/CautelaProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/CellocatorFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/CellocatorProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/CellocatorProtocolEncoderTest.java6
-rw-r--r--src/test/java/org/traccar/protocol/CguardProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/CityeasyProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/CityeasyProtocolEncoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/ContinentalProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/CradlepointProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/DingtekFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/DingtekProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/DishaProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/DmtHttpProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/DmtProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/DolphinProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/DraginoProtocolDecoderTest.java20
-rw-r--r--src/test/java/org/traccar/protocol/Dsf22FrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Dsf22ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/DualcamFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/DualcamProtocolDecoderTest.java5
-rw-r--r--src/test/java/org/traccar/protocol/DwayProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/EasyTrackProtocolDecoderTest.java5
-rw-r--r--src/test/java/org/traccar/protocol/EasyTrackProtocolEncoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/EelinkProtocolEncoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/EgtsFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/EgtsProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/EnforaProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/EnnfuProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/EnvotechProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/EsealProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/EsealProtocolEncoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/EskyFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/EskyProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/ExtremTracProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/FifotrackFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java9
-rw-r--r--src/test/java/org/traccar/protocol/FifotrackProtocolEncoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/FleetGuideProtocolDecoderTest.java33
-rw-r--r--src/test/java/org/traccar/protocol/FlespiProtocolDecoderTest.java17
-rw-r--r--src/test/java/org/traccar/protocol/FlexApiProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/FlexCommProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/FlexibleReportProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/FlextrackProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/FoxProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/FreedomProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/FreematicsProtocolDecoderTest.java5
-rw-r--r--src/test/java/org/traccar/protocol/FutureWayFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/FutureWayProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/G1rusProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/GalileoFrameDecoderTest.java8
-rw-r--r--src/test/java/org/traccar/protocol/GalileoProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/GalileoProtocolEncoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/GatorProtocolDecoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/GatorProtocolEncoderTest.java23
-rw-r--r--src/test/java/org/traccar/protocol/GenxProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Gl100ProtocolDecoderTest.java5
-rw-r--r--src/test/java/org/traccar/protocol/Gl200BinaryProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Gl200FrameDecoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java66
-rw-r--r--src/test/java/org/traccar/protocol/GlobalSatProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/GlobalSatProtocolEncoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/GlobalstarProtocolDecoderTest.java6
-rw-r--r--src/test/java/org/traccar/protocol/GnxProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/GoSafeProtocolDecoderTest.java5
-rw-r--r--src/test/java/org/traccar/protocol/GotopProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Gps056FrameDecoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/Gps056ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Gps103ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Gps103ProtocolEncoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/GpsGateProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/GpsMarkerProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/GpsmtaProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/GranitFrameDecoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/GranitProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Gs100ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Gt02ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Gt06FrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java59
-rw-r--r--src/test/java/org/traccar/protocol/Gt06ProtocolEncoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Gt30ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/H02FrameDecoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/H02ProtocolEncoderTest.java8
-rw-r--r--src/test/java/org/traccar/protocol/HaicomProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/HomtecsProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/HoopoProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/HuaShengFrameDecoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java12
-rw-r--r--src/test/java/org/traccar/protocol/HuaShengProtocolEncoderTest.java33
-rw-r--r--src/test/java/org/traccar/protocol/HuabaoFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java51
-rw-r--r--src/test/java/org/traccar/protocol/HuabaoProtocolEncoderTest.java6
-rw-r--r--src/test/java/org/traccar/protocol/HunterProProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/IdplProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/IntellitracProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/IotmProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/ItsFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/ItsProtocolEncoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/Ivt401ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/JidoProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/JpKorjarProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/JsonFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java10
-rw-r--r--src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Jt600ProtocolEncoderTest.java4
-rwxr-xr-xsrc/test/java/org/traccar/protocol/KenjiProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/KhdProtocolDecoderTest.java11
-rw-r--r--src/test/java/org/traccar/protocol/KhdProtocolEncoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/L100FrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/L100ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/LacakProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/LaipacProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/LeafSpyProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/M2cProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/M2mProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/MaestroProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/ManPowerProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Mavlink2ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/MegastekFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/MegastekProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/MeiligaoFrameDecoderTest.java6
-rw-r--r--src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/MeiligaoProtocolEncoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/MeitrackFrameDecoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java32
-rw-r--r--src/test/java/org/traccar/protocol/MeitrackProtocolEncoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/MilesmateProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/MiniFinderProtocolDecoderTest.java6
-rw-r--r--src/test/java/org/traccar/protocol/MiniFinderProtocolEncoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/Minifinder2ProtocolEncoderTest.java28
-rw-r--r--src/test/java/org/traccar/protocol/MobilogixProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/MoovboxProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/MotorProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/MtxProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/MxtProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/NavigilProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/NavisFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/NavisProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/NavisetFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/NavisetProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/NavtelecomFrameDecoderTest.java6
-rw-r--r--src/test/java/org/traccar/protocol/NavtelecomProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/NdtpV6ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/NeosProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/NetProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/NiotProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/NoranProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/NoranProtocolEncoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/NtoProtocolDecoderTest.java19
-rw-r--r--src/test/java/org/traccar/protocol/NvsFrameDecoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/NvsProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/NyitechProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/ObdDongleProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/OigoProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/OkoProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/OmnicommFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/OmnicommProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/OpenGtsProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/OrbcommProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/OrionProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/OsmAndProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/OutsafeProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/OwnTracksProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/PacificTrackProtocolDecoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/PathAwayProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/PiligrimProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/PluginProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/PolteProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/PortmanProtocolDecoderTest.java5
-rw-r--r--src/test/java/org/traccar/protocol/PortmanProtocolEncoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/PositrexProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/PretraceProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/PretraceProtocolEncoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/PricolProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/ProgressProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/PstFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/PstFrameEncoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/PstProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/PstProtocolEncoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Pt215ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Pt3000ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Pt502FrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Pt502ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Pt502ProtocolEncoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/Pt60ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/PuiProtocolDecoderTest.java23
-rw-r--r--src/test/java/org/traccar/protocol/R12wProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/RaceDynamicsProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/RadarProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/RamacProtocolDecoderTest.java26
-rw-r--r--src/test/java/org/traccar/protocol/RaveonProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/RecodaProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/RetranslatorProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/RfTrackProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/RitiProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/RoboTrackFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/RoboTrackProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/RstProtocolDecoderTest.java14
-rw-r--r--src/test/java/org/traccar/protocol/RuptelaProtocolDecoderTest.java5
-rw-r--r--src/test/java/org/traccar/protocol/RuptelaProtocolEncoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/S168ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/SabertekFrameDecoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/SabertekProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/SanavProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/SanulProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/SatsolProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java10
-rw-r--r--src/test/java/org/traccar/protocol/SiwiProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/SkypatrolProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/SmartSoleProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/SmokeyProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/SolarPoweredProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/SpotProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/StarcomProtocolDecoderTest.java5
-rw-r--r--src/test/java/org/traccar/protocol/StartekFrameDecoderTest.java23
-rw-r--r--src/test/java/org/traccar/protocol/StartekProtocolDecoderTest.java42
-rw-r--r--src/test/java/org/traccar/protocol/StartekProtocolEncoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/StbProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Stl060ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/SuntechFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java27
-rwxr-xr-xsrc/test/java/org/traccar/protocol/SupermateProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/SviasProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/SwiftechProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java14
-rw-r--r--src/test/java/org/traccar/protocol/T57FrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/T57ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/T622IridiumProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java8
-rw-r--r--src/test/java/org/traccar/protocol/T800xProtocolEncoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/TaipProtocolDecoderTest.java14
-rw-r--r--src/test/java/org/traccar/protocol/TechTltProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/TechtoCruzFrameDecoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/TechtoCruzProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/TekFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/TekProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/TelemaxProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/TelicFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/TelicProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/TeltonikaFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/TeltonikaProtocolEncoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/TeraTrackProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/ThinkPowerProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/ThinkRaceProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/ThurayaProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Tk102ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Tk103FrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Tk103ProtocolDecoderTest.java9
-rw-r--r--src/test/java/org/traccar/protocol/Tk103ProtocolEncoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/TlvProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/TmgFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/TmgProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/TopflytechProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/TopinProtocolDecoderTest.java5
-rw-r--r--src/test/java/org/traccar/protocol/TopinProtocolEncoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/TotemFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/TotemProtocolDecoderTest.java7
-rw-r--r--src/test/java/org/traccar/protocol/TotemProtocolEncoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/Tr20ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Tr900ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/TrackboxProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/TrakMateProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/TramigoFrameDecoderTest.java6
-rw-r--r--src/test/java/org/traccar/protocol/TramigoProtocolDecoderTest.java8
-rw-r--r--src/test/java/org/traccar/protocol/TranSyncProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/TrvProtocolDecoderTest.java7
-rw-r--r--src/test/java/org/traccar/protocol/Tt8850ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/TytanProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java7
-rw-r--r--src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/UlbotechProtocolEncoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/UproProtocolDecoderTest.java10
-rw-r--r--src/test/java/org/traccar/protocol/UuxProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/V680ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/ValtrackProtocolDecoderTest.java19
-rw-r--r--src/test/java/org/traccar/protocol/VisiontekProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/VltProtocolDecoderTest.java22
-rw-r--r--src/test/java/org/traccar/protocol/VnetProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Vt200FrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Vt200ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/VtfmsFrameDecoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/VtfmsProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/WatchFrameDecoderTest.java10
-rw-r--r--src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java9
-rw-r--r--src/test/java/org/traccar/protocol/WatchProtocolEncoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/WialonProtocolDecoderTest.java9
-rw-r--r--src/test/java/org/traccar/protocol/WliFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/WliProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/WondexFrameDecoderTest.java6
-rw-r--r--src/test/java/org/traccar/protocol/WondexProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/WondexProtocolEncoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/WristbandProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Xexun2FrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Xexun2FrameEncoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Xexun2ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Xexun2ProtocolEncoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/XexunFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/XexunProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/XirgoProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/XirgoProtocolEncoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/Xrb28ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Xrb28ProtocolEncoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/Xt013ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/Xt2400ProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/YwtProtocolDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/reports/ReportUtilsTest.java155
-rw-r--r--src/test/java/org/traccar/speedlimit/OverpassSpeedLimitProviderTest.java18
-rw-r--r--src/test/java/org/traccar/web/WebServerTest.java2
-rw-r--r--swagger.json143
-rw-r--r--templates/export/devices.xlsxbin0 -> 9282 bytes
-rw-r--r--templates/full/alarm.vm80
-rw-r--r--templates/full/deviceExpiration.vm7
-rw-r--r--templates/full/deviceExpirationReminder.vm7
-rw-r--r--templates/full/deviceOffline.vm1
-rw-r--r--templates/full/deviceOnline.vm1
-rw-r--r--templates/full/deviceUnknown.vm1
-rw-r--r--templates/full/queuedCommandSent.vm11
-rw-r--r--templates/full/unknown.vm7
-rw-r--r--templates/full/userExpiration.vm7
-rw-r--r--templates/full/userExpirationReminder.vm7
-rw-r--r--templates/short/alarm.vm80
-rw-r--r--templates/short/queuedCommandSent.vm2
-rw-r--r--templates/short/unknown.vm2
-rwxr-xr-xtools/test-commands.py4
-rwxr-xr-xtools/test-integration.py40
-rwxr-xr-xtools/test-map.py6
m---------traccar-web0
990 files changed, 13402 insertions, 4389 deletions
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 03a3831b9..ba524068f 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -36,7 +36,7 @@ Provide as much details as possible, including log fragments, operating system a
Before creating a feature request make sure that the feature or modification that you are requesting is not yet implemented.
-Search reposiroty to ensure that there is no existing issues for your request. If there is, add a new comment on that issue.
+Search repository to ensure that there is no existing issues for your request. If there is, add a new comment on that issue.
Provide as much details as possible, including use case for your feature and any benefits that you can think of.
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 4ceb88a7c..a8e4c5369 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -11,7 +11,7 @@ on:
jobs:
build:
- runs-on: ubuntu-18.04
+ runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
@@ -28,13 +28,13 @@ jobs:
- run: ./gradlew build
- uses: actions/setup-node@v3
with:
- node-version: 14
+ node-version: 18
cache: npm
cache-dependency-path: |
traccar-web/package-lock.json
traccar-web/modern/package-lock.json
- run: |
- wget -q http://cdn.sencha.com/cmd/7.1.0.15/no-jre/SenchaCmd-7.1.0.15-linux-i386.sh.zip
+ wget -q https://trials.sencha.com/cmd/7.6.0/SenchaCmd-7.6.0.87-linux-amd64.sh.zip
unzip SenchaCmd-*.zip
./SenchaCmd-*.sh -q
echo "$HOME/bin/Sencha/Cmd/" >> $GITHUB_PATH
@@ -42,14 +42,16 @@ jobs:
- run: |
sudo dpkg --add-architecture i386
sudo apt-get update
+ sudo apt-get install libgcc-s1:i386 libstdc++6:i386
sudo apt-get install innoextract makeself wine32 s3cmd
- name: Build installers
working-directory: ./setup
run: |
wget -q http://files.jrsoftware.org/is/5/isetup-5.5.6.exe
- wget -q https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.4+8/OpenJDK17U-jdk_x64_windows_hotspot_17.0.4_8.zip
- wget -q https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.4+8/OpenJDK17U-jdk_x64_linux_hotspot_17.0.4_8.tar.gz
- wget -q https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.4+8/OpenJDK17U-jdk_arm_linux_hotspot_17.0.4_8.tar.gz
+ wget -q https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.6+10/OpenJDK17U-jdk_x64_windows_hotspot_17.0.6_10.zip
+ wget -q https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.6+10/OpenJDK17U-jdk_x64_linux_hotspot_17.0.6_10.tar.gz
+ wget -q https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.6+10/OpenJDK17U-jdk_arm_linux_hotspot_17.0.6_10.tar.gz
+ wget -q https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.6+10/OpenJDK17U-jdk_aarch64_linux_hotspot_17.0.6_10.tar.gz
./package.sh ${{ github.event.inputs.version }}
- name: Upload installers
working-directory: ./setup
diff --git a/.gitignore b/.gitignore
index c005ef9e4..2d03a9625 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,5 @@ nbactions.xml
.gradle
out
build
+.vscode
+bin
diff --git a/build.gradle b/build.gradle
index 91ba2cd5c..3677f8f94 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,57 +1,61 @@
plugins {
id "java"
id "checkstyle"
- id "com.google.protobuf" version "0.8.19"
- id "org.kordamp.gradle.project-enforcer" version "0.10.0"
+ id "com.google.protobuf" version "0.9.4"
+ id "org.kordamp.gradle.project-enforcer" version "0.13.0"
}
repositories {
mavenCentral()
}
-ext {
- guiceVersion = "5.1.0"
- jettyVersion = "10.0.12" // jetty 11 javax to jakarta
- jerseyVersion = "2.37" // jersey 3 javax to jakarta
- jacksonVersion = "2.13.4" // same version as jersey-media-json-jackson dependency
- protobufVersion = "3.21.9"
-}
-
sourceCompatibility = "11"
compileJava.options.encoding = "UTF-8"
jar.destinationDirectory = file("$projectDir/target")
checkstyle {
- toolVersion = "10.3.4"
+ toolVersion = "10.12.5"
configFile = "gradle/checkstyle.xml" as File
checkstyleTest.enabled = false
}
-protobuf {
- protoc {
- artifact = "com.google.protobuf:protoc:$protobufVersion"
- }
-}
-
enforce {
rule(enforcer.rules.EnforceBytecodeVersion) { r ->
r.maxJdkVersion = "11"
}
}
+ext {
+ guiceVersion = "7.0.0"
+ jettyVersion = "11.0.19"
+ jerseyVersion = "3.1.5"
+ jacksonVersion = "2.15.3" // same version as jersey-media-json-jackson dependency
+ protobufVersion = "3.25.2"
+ jxlsVersion = "2.14.0"
+ junitVersion = "5.10.1"
+}
+
+protobuf {
+ protoc {
+ artifact = "com.google.protobuf:protoc:$protobufVersion"
+ }
+}
+
dependencies {
- implementation "commons-codec:commons-codec:1.15"
- implementation "com.h2database:h2:2.1.214"
- implementation "com.mysql:mysql-connector-j:8.0.31"
- implementation "org.postgresql:postgresql:42.5.1"
- implementation "com.microsoft.sqlserver:mssql-jdbc:11.2.1.jre11"
- implementation "com.zaxxer:HikariCP:5.0.1"
- implementation "io.netty:netty-all:4.1.85.Final"
- implementation "org.slf4j:slf4j-jdk14:2.0.5"
+ implementation "commons-codec:commons-codec:1.16.0"
+ implementation "com.h2database:h2:2.2.224"
+ implementation "com.mysql:mysql-connector-j:8.2.0"
+ implementation "org.mariadb.jdbc:mariadb-java-client:3.3.2"
+ implementation "org.postgresql:postgresql:42.7.1"
+ implementation "com.microsoft.sqlserver:mssql-jdbc:12.4.2.jre11"
+ implementation "com.zaxxer:HikariCP:5.1.0"
+ implementation "io.netty:netty-all:4.1.104.Final"
+ implementation "org.slf4j:slf4j-jdk14:2.0.11"
implementation "com.google.inject:guice:$guiceVersion"
implementation "com.google.inject.extensions:guice-servlet:$guiceVersion"
implementation "org.owasp.encoder:encoder:1.2.3"
- implementation "org.glassfish:jakarta.json:1.1.6"
+ implementation "org.glassfish:jakarta.json:2.0.1"
+ implementation "com.sun.mail:jakarta.mail:2.0.1"
implementation "org.eclipse.jetty:jetty-server:$jettyVersion"
implementation "org.eclipse.jetty:jetty-servlet:$jettyVersion"
implementation "org.eclipse.jetty:jetty-servlets:$jettyVersion"
@@ -62,35 +66,37 @@ dependencies {
implementation "org.glassfish.jersey.containers:jersey-container-servlet:$jerseyVersion"
implementation "org.glassfish.jersey.media:jersey-media-json-jackson:$jerseyVersion"
implementation "org.glassfish.jersey.inject:jersey-hk2:$jerseyVersion"
- implementation "org.glassfish.hk2:guice-bridge:2.6.1" // same version as jersey-hk2
+ implementation "org.glassfish.hk2:guice-bridge:3.0.5" // same version as jersey-hk2
implementation "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:$jacksonVersion"
- implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr353:$jacksonVersion"
- implementation "org.liquibase:liquibase-core:4.17.2"
- implementation "com.sun.mail:jakarta.mail:1.6.7"
- implementation "org.jxls:jxls:2.4.7" // needs upgrade (wait for jexl 4)
- implementation "org.jxls:jxls-poi:1.0.16" // needs upgrade (wait for jexl 4)
- implementation "org.apache.velocity:velocity:1.7" // needs upgrade
- implementation "org.apache.velocity:velocity-tools:2.0" // needs upgrade
+ implementation "com.fasterxml.jackson.datatype:jackson-datatype-jakarta-jsonp:$jacksonVersion"
+ implementation "org.liquibase:liquibase-core:4.23.2" // upgrade has issues
+ implementation "org.apache.commons:commons-jexl3:3.3"
+ implementation "org.jxls:jxls:$jxlsVersion"
+ implementation "org.jxls:jxls-poi:$jxlsVersion"
+ implementation "org.apache.velocity:velocity-engine-core:2.3"
+ implementation "org.apache.velocity.tools:velocity-tools-generic:3.1"
implementation "org.apache.commons:commons-collections4:4.4"
- implementation "org.mnode.ical4j:ical4j:3.2.7"
+ implementation "org.mnode.ical4j:ical4j:3.2.14"
implementation "org.locationtech.spatial4j:spatial4j:0.8"
implementation "org.locationtech.jts:jts-core:1.19.0"
- implementation "net.java.dev.jna:jna-platform:5.12.1"
- implementation "com.github.jnr:jnr-posix:3.1.16"
+ implementation "net.java.dev.jna:jna-platform:5.14.0"
+ implementation "com.github.jnr:jnr-posix:3.1.18"
implementation "com.google.protobuf:protobuf-java:$protobufVersion"
- implementation "javax.xml.bind:jaxb-api:2.3.1"
- implementation "com.sun.xml.bind:jaxb-core:3.0.2" // needs upgrade
- implementation "com.sun.xml.bind:jaxb-impl:3.0.2" // needs upgrade
- implementation "javax.activation:activation:1.1.1"
- implementation "com.amazonaws:aws-java-sdk-sns:1.12.349"
- implementation "org.apache.kafka:kafka-clients:3.3.1"
- implementation 'com.hivemq:hivemq-mqtt-client:1.3.0'
- implementation("com.google.firebase:firebase-admin:9.1.1") {
- exclude group: 'com.google.cloud', module: 'google-cloud-firestore'
- exclude group: 'com.google.cloud', module: 'google-cloud-storage'
- }
- testImplementation "junit:junit:4.13.2"
- testImplementation "org.mockito:mockito-core:4.+"
+ implementation "com.amazonaws:aws-java-sdk-sns:1.12.636"
+ implementation "org.apache.kafka:kafka-clients:3.6.1"
+ implementation "com.hivemq:hivemq-mqtt-client:1.3.3"
+ implementation "redis.clients:jedis:5.1.0"
+ implementation "com.google.firebase:firebase-admin:9.2.0"
+ implementation "com.nimbusds:oauth2-oidc-sdk:11.9.1"
+ implementation "com.rabbitmq:amqp-client:5.20.0"
+ implementation "com.warrenstrange:googleauth:1.5.0"
+ testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
+ testImplementation "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
+ testImplementation "org.mockito:mockito-core:5.8.0"
+}
+
+test {
+ useJUnitPlatform()
}
task copyDependencies(type: Copy) {
@@ -103,7 +109,7 @@ jar {
manifest {
attributes(
"Main-Class": "org.traccar.Main",
- "Implementation-Version": "5.5",
+ "Implementation-Version": "5.12",
"Class-Path": configurations.runtimeClasspath.files.collect { "lib/$it.name" }.join(" "))
}
}
diff --git a/gradle/checkstyle.xml b/gradle/checkstyle.xml
index a6a6f0ff7..bb89450d6 100644
--- a/gradle/checkstyle.xml
+++ b/gradle/checkstyle.xml
@@ -114,7 +114,7 @@
<module name="EmptyStatement"/>
<module name="EqualsHashCode"/>
<module name="IllegalInstantiation"/>
- <module name="InnerAssignment"/>
+ <!--<module name="InnerAssignment"/>-->
<module name="MissingSwitchDefault"/>
<module name="SimplifyBooleanExpression"/>
<module name="SimplifyBooleanReturn"/>
diff --git a/schema/changelog-5.10.xml b/schema/changelog-5.10.xml
new file mode 100644
index 000000000..63988b14a
--- /dev/null
+++ b/schema/changelog-5.10.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<databaseChangeLog
+ xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
+ http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd"
+ logicalFilePath="changelog-5.10">
+
+ <changeSet author="author" id="changelog-5.10">
+
+ <addColumn tableName="tc_users">
+ <column name="totpkey" type="VARCHAR(64)" />
+ </addColumn>
+
+ </changeSet>
+
+</databaseChangeLog>
diff --git a/schema/changelog-5.11.xml b/schema/changelog-5.11.xml
new file mode 100644
index 000000000..e59df9249
--- /dev/null
+++ b/schema/changelog-5.11.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<databaseChangeLog
+ xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
+ http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd"
+ logicalFilePath="changelog-5.11">
+
+ <changeSet author="author" id="changelog-5.11">
+
+ <addColumn tableName="tc_users">
+ <column name="temporary" type="BOOLEAN" defaultValueBoolean="false" />
+ </addColumn>
+
+ </changeSet>
+
+</databaseChangeLog>
diff --git a/schema/changelog-5.7.xml b/schema/changelog-5.7.xml
new file mode 100644
index 000000000..ad15ac48c
--- /dev/null
+++ b/schema/changelog-5.7.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<databaseChangeLog
+ xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
+ http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd"
+ logicalFilePath="changelog-5.7">
+
+ <changeSet author="author" id="changelog-5.7">
+
+ <addColumn tableName="tc_notifications">
+ <column name="commandid" type="INT" />
+ </addColumn>
+
+ <addForeignKeyConstraint
+ baseTableName="tc_notifications"
+ baseColumnNames="commandid"
+ constraintName="fk_notifications_commandid"
+ referencedTableName="tc_commands"
+ referencedColumnNames="id"
+ onDelete="CASCADE" />
+
+ </changeSet>
+
+</databaseChangeLog>
diff --git a/schema/changelog-5.8.xml b/schema/changelog-5.8.xml
new file mode 100644
index 000000000..ec54bf17f
--- /dev/null
+++ b/schema/changelog-5.8.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<databaseChangeLog
+ xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
+ http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd"
+ logicalFilePath="changelog-5.8">
+
+ <changeSet author="author" id="changelog-5.8">
+
+ <dropColumn tableName="tc_devices" columnName="geofenceids" />
+ <addColumn tableName="tc_positions">
+ <column name="geofenceids" type="VARCHAR(128)" />
+ </addColumn>
+
+ </changeSet>
+
+</databaseChangeLog>
diff --git a/schema/changelog-5.9.xml b/schema/changelog-5.9.xml
new file mode 100644
index 000000000..50a3d3aaa
--- /dev/null
+++ b/schema/changelog-5.9.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<databaseChangeLog
+ xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
+ http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd"
+ logicalFilePath="changelog-5.9">
+
+ <changeSet author="author" id="changelog-5.9">
+
+ <addColumn tableName="tc_devices">
+ <column name="calendarid" type="INT" />
+ </addColumn>
+
+ <addForeignKeyConstraint
+ baseTableName="tc_devices"
+ baseColumnNames="calendarid"
+ constraintName="fk_devices_calendarid"
+ referencedTableName="tc_calendars"
+ referencedColumnNames="id"
+ onDelete="SET NULL"
+ onUpdate="RESTRICT"
+ />
+
+ </changeSet>
+
+</databaseChangeLog>
diff --git a/schema/changelog-master.xml b/schema/changelog-master.xml
index 0835918c9..559d90923 100644
--- a/schema/changelog-master.xml
+++ b/schema/changelog-master.xml
@@ -37,5 +37,10 @@
<include file="changelog-5.4.xml" relativeToChangelogFile="true" />
<include file="changelog-5.5.xml" relativeToChangelogFile="true" />
<include file="changelog-5.6.xml" relativeToChangelogFile="true" />
+ <include file="changelog-5.7.xml" relativeToChangelogFile="true" />
+ <include file="changelog-5.8.xml" relativeToChangelogFile="true" />
+ <include file="changelog-5.9.xml" relativeToChangelogFile="true" />
+ <include file="changelog-5.10.xml" relativeToChangelogFile="true" />
+ <include file="changelog-5.11.xml" relativeToChangelogFile="true" />
</databaseChangeLog>
diff --git a/setup/cloud-init.yaml b/setup/cloud-init.yaml
new file mode 100644
index 000000000..1246fb1b7
--- /dev/null
+++ b/setup/cloud-init.yaml
@@ -0,0 +1,29 @@
+#cloud-config
+
+write_files:
+ - content: |
+ <?xml version='1.0' encoding='UTF-8'?>
+ <!DOCTYPE properties SYSTEM 'http://java.sun.com/dtd/properties.dtd'>
+ <properties>
+
+ <entry key="config.default">./conf/default.xml</entry>
+
+ <entry key='database.driver'>com.mysql.jdbc.Driver</entry>
+ <entry key='database.url'>jdbc:mysql://localhost/traccar?zeroDateTimeBehavior=round&amp;serverTimezone=UTC&amp;allowPublicKeyRetrieval=true&amp;useSSL=false&amp;allowMultiQueries=true&amp;autoReconnect=true&amp;useUnicode=yes&amp;characterEncoding=UTF-8&amp;sessionVariables=sql_mode=''</entry>
+ <entry key='database.user'>root</entry>
+ <entry key='database.password'>root</entry>
+
+ </properties>
+ path: /root/traccar.xml
+
+package_update: true
+packages:
+ - unzip
+ - mysql-server
+
+runcmd:
+ - mysql -u root --execute="ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'root'; GRANT ALL ON *.* TO 'root'@'localhost' WITH GRANT OPTION; FLUSH PRIVILEGES; CREATE DATABASE traccar;"
+ - wget https://www.traccar.org/download/traccar-linux-64-latest.zip
+ - unzip traccar-linux-*.zip && ./traccar.run
+ - cp /root/traccar.xml /opt/traccar/conf/
+ - service traccar start
diff --git a/setup/default.xml b/setup/default.xml
index c00d29384..95bb1f04b 100644
--- a/setup/default.xml
+++ b/setup/default.xml
@@ -14,6 +14,7 @@
<entry key='web.path'>./modern</entry>
<entry key='web.sanitize'>false</entry>
<entry key='web.persistSession'>false</entry>
+ <entry key='web.showUnknownDevices'>true</entry>
<entry key='geocoder.enable'>true</entry>
<entry key='geocoder.type'>locationiq</entry>
@@ -32,7 +33,7 @@
<entry key='media.path'>./media</entry>
- <entry key='notificator.types'>web,mail</entry>
+ <entry key='notificator.types'>web,mail,command</entry>
<entry key='server.statistics'>https://www.traccar.org/analytics/</entry>
@@ -287,5 +288,15 @@
<entry key='ndtpv6.port'>5243</entry>
<entry key='g1rus.port'>5244</entry>
<entry key='rftrack.port'>5245</entry>
+ <entry key='vlt.port'>5246</entry>
+ <entry key='transync.port'>5247</entry>
+ <entry key='t622iridium.port'>5248</entry>
+ <entry key='pui.port'>5249</entry>
+ <entry key='nto.port'>5250</entry>
+ <entry key='ramac.port'>5251</entry>
+ <entry key='positrex.port'>5252</entry>
+ <entry key='dragino.port'>5253</entry>
+ <entry key='fleetguide.port'>5254</entry>
+ <entry key='valtrack.port'>5255</entry>
</properties>
diff --git a/setup/package.sh b/setup/package.sh
index 71b86014f..f8ec927eb 100755
--- a/setup/package.sh
+++ b/setup/package.sh
@@ -15,6 +15,7 @@ usage () {
echo "Available platforms:"
echo " * linux-64"
echo " * linux-arm"
+ echo " * linux-arm64"
echo " * windows-64"
echo " * other"
exit 1
@@ -64,15 +65,18 @@ if [ $PLATFORM = "all" -o $PLATFORM = "windows-64" ]; then
check_requirement "Windows 64 Java" "ls OpenJDK*64_windows*.zip" "Missing Windows 64 JDK (https://adoptium.net/)"
check_requirement "Wine" "which wine" "Missing wine binary"
fi
-if [ $PLATFORM = "all" -o $PLATFORM = "linux-64" -o $PLATFORM = "linux-arm" ]; then
+if [ $PLATFORM = "all" -o $PLATFORM = "linux-64" -o $PLATFORM = "linux-arm" -o $PLATFORM = "linux-arm64" ]; then
check_requirement "Makeself" "which makeself" "Missing makeself binary"
fi
if [ $PLATFORM = "all" -o $PLATFORM = "linux-64" ]; then
- check_requirement "Linux 64 Java" "ls OpenJDK*64_linux*.tar.gz" "Missing Linux 64 JDK (https://adoptium.net/)"
+ check_requirement "Linux 64 Java" "ls OpenJDK*x64_linux*.tar.gz" "Missing Linux 64 JDK (https://adoptium.net/)"
fi
if [ $PLATFORM = "all" -o $PLATFORM = "linux-arm" ]; then
check_requirement "Linux ARM Java" "ls OpenJDK*arm_linux*.tar.gz" "Missing Linux ARM JDK (https://adoptium.net/)"
fi
+if [ $PLATFORM = "all" -o $PLATFORM = "linux-arm64" ]; then
+ check_requirement "Linux ARM 64 Java" "ls OpenJDK*aarch64_linux*.tar.gz" "Missing Linux ARM 64 JDK (https://adoptium.net/)"
+fi
if [ $PREREQ = false ]; then
info "Missing build requirements, aborting..."
exit 1
@@ -120,7 +124,7 @@ package_other () {
package_windows () {
info "Building Windows 64 installer"
unzip -q OpenJDK*64_windows*.zip
- jlink --module-path jdk-*/jmods --add-modules java.se,jdk.charsets,jdk.crypto.ec --output out/jre
+ jlink --module-path jdk-*/jmods --add-modules java.se,jdk.charsets,jdk.crypto.ec,jdk.unsupported --output out/jre
rm -rf jdk-*
wine app/ISCC.exe traccar.iss >/dev/null
rm -rf out/jre
@@ -133,8 +137,8 @@ package_linux () {
cp setup.sh out
cp traccar.service out
- tar -xf OpenJDK*$1_linux*.tar.gz
- jlink --module-path jdk-*/jmods --add-modules java.se,jdk.charsets,jdk.crypto.ec --output out/jre
+ tar -xf OpenJDK*$2_linux*.tar.gz
+ jlink --module-path jdk-*/jmods --add-modules java.se,jdk.charsets,jdk.crypto.ec,jdk.unsupported --output out/jre
rm -rf jdk-*
makeself --needroot --quiet --notemp out traccar.run "traccar" ./setup.sh
rm -rf out/jre
@@ -148,22 +152,29 @@ package_linux () {
package_linux_64 () {
info "Building Linux 64 installer"
- package_linux 64
+ package_linux 64 x64
ok "Created Linux 64 installer"
}
package_linux_arm () {
info "Building Linux ARM installer"
- package_linux arm
+ package_linux arm arm
ok "Created Linux ARM installer"
}
+package_linux_arm64 () {
+ info "Building Linux ARM 64 installer"
+ package_linux arm64 aarch64
+ ok "Created Linux ARM 64 installer"
+}
+
prepare
case $PLATFORM in
all)
package_linux_64
package_linux_arm
+ package_linux_arm64
package_windows
package_other
;;
@@ -176,6 +187,10 @@ case $PLATFORM in
package_linux_arm
;;
+ linux-arm64)
+ package_linux_arm64
+ ;;
+
windows-64)
package_windows
;;
diff --git a/setup/traccar.iss b/setup/traccar.iss
index 28b33adfb..2ccee1c3e 100644
--- a/setup/traccar.iss
+++ b/setup/traccar.iss
@@ -1,6 +1,6 @@
[Setup]
AppName=Traccar
-AppVersion=5.5
+AppVersion=5.12
DefaultDirName={pf}\Traccar
OutputBaseFilename=traccar-setup
ArchitecturesInstallIn64BitMode=x64
diff --git a/src/main/java/org/traccar/BaseMqttProtocolDecoder.java b/src/main/java/org/traccar/BaseMqttProtocolDecoder.java
new file mode 100644
index 000000000..0388563f5
--- /dev/null
+++ b/src/main/java/org/traccar/BaseMqttProtocolDecoder.java
@@ -0,0 +1,96 @@
+/*
+ * 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;
+
+import io.netty.channel.Channel;
+import io.netty.handler.codec.mqtt.MqttConnectMessage;
+import io.netty.handler.codec.mqtt.MqttConnectReturnCode;
+import io.netty.handler.codec.mqtt.MqttMessage;
+import io.netty.handler.codec.mqtt.MqttMessageBuilders;
+import io.netty.handler.codec.mqtt.MqttPublishMessage;
+import io.netty.handler.codec.mqtt.MqttSubscribeMessage;
+import org.traccar.session.DeviceSession;
+
+import java.net.SocketAddress;
+
+public abstract class BaseMqttProtocolDecoder extends BaseProtocolDecoder {
+
+ public BaseMqttProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ protected abstract Object decode(DeviceSession deviceSession, MqttPublishMessage message) throws Exception;
+
+ @Override
+ protected final Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ if (msg instanceof MqttConnectMessage) {
+
+ MqttConnectMessage message = (MqttConnectMessage) msg;
+
+ DeviceSession deviceSession = getDeviceSession(
+ channel, remoteAddress, message.payload().clientIdentifier());
+
+ MqttConnectReturnCode returnCode = deviceSession != null
+ ? MqttConnectReturnCode.CONNECTION_ACCEPTED
+ : MqttConnectReturnCode.CONNECTION_REFUSED_IDENTIFIER_REJECTED;
+
+ MqttMessage response = MqttMessageBuilders.connAck().returnCode(returnCode).build();
+
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+
+ } else if (msg instanceof MqttSubscribeMessage) {
+
+ MqttSubscribeMessage message = (MqttSubscribeMessage) msg;
+
+ MqttMessage response = MqttMessageBuilders.subAck()
+ .packetId(message.variableHeader().messageId())
+ .build();
+
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+
+ } else if (msg instanceof MqttPublishMessage) {
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ MqttPublishMessage message = (MqttPublishMessage) msg;
+
+ Object result = decode(deviceSession, message);
+
+ MqttMessage response = MqttMessageBuilders.pubAck()
+ .packetId(message.variableHeader().packetId())
+ .build();
+
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+
+ return result;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/BasePipelineFactory.java b/src/main/java/org/traccar/BasePipelineFactory.java
index b184da45c..ca4a4ae63 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.
@@ -25,6 +25,7 @@ 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;
@@ -32,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;
@@ -59,16 +62,20 @@ 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, Config config, String protocol) {
this.injector = Main.getInjector();
this.connector = connector;
+ this.config = config;
this.protocol = protocol;
- timeout = config.getInteger(Keys.PROTOCOL_TIMEOUT.withPrefix(protocol));
+ int timeout = config.getInteger(Keys.PROTOCOL_TIMEOUT.withPrefix(protocol));
if (timeout == 0) {
- timeout = config.getInteger(Keys.SERVER_TIMEOUT);
+ this.timeout = config.getInteger(Keys.SERVER_TIMEOUT);
+ } else {
+ this.timeout = timeout;
}
}
@@ -110,8 +117,21 @@ 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));
+
+ var loggingHandler = new StandardLoggingHandler(protocol);
+ injector.injectMembers(loggingHandler);
+ pipeline.addLast(loggingHandler);
+
+ if (!connector.isDatagram() && !config.getBoolean(Keys.SERVER_INSTANT_ACKNOWLEDGEMENT)) {
+ pipeline.addLast(new AcknowledgementHandler());
+ }
addProtocolHandlers(handler -> {
if (handler instanceof BaseProtocolDecoder || handler instanceof BaseProtocolEncoder) {
@@ -134,6 +154,7 @@ public abstract class BasePipelineFactory extends ChannelInitializer<Channel> {
DistanceHandler.class,
RemoteAddressHandler.class,
FilterHandler.class,
+ GeofenceHandler.class,
GeocoderHandler.class,
SpeedLimitHandler.class,
MotionHandler.class,
diff --git a/src/main/java/org/traccar/BaseProtocol.java b/src/main/java/org/traccar/BaseProtocol.java
index d19fc307e..ea302997c 100644
--- a/src/main/java/org/traccar/BaseProtocol.java
+++ b/src/main/java/org/traccar/BaseProtocol.java
@@ -23,8 +23,8 @@ import org.traccar.helper.DataConverter;
import org.traccar.model.Command;
import org.traccar.sms.SmsManager;
-import javax.annotation.Nullable;
-import javax.inject.Inject;
+import jakarta.annotation.Nullable;
+import jakarta.inject.Inject;
import java.net.SocketAddress;
import java.util.Arrays;
import java.util.Collection;
@@ -105,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));
diff --git a/src/main/java/org/traccar/BaseProtocolDecoder.java b/src/main/java/org/traccar/BaseProtocolDecoder.java
index 382daf92f..495a866c0 100644
--- a/src/main/java/org/traccar/BaseProtocolDecoder.java
+++ b/src/main/java/org/traccar/BaseProtocolDecoder.java
@@ -29,9 +29,8 @@ 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 javax.inject.Inject;
+import jakarta.inject.Inject;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Collection;
@@ -52,6 +51,8 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
private MediaManager mediaManager;
private CommandsManager commandsManager;
+ private String modelOverride;
+
public BaseProtocolDecoder(Protocol protocol) {
this.protocol = protocol;
}
@@ -125,22 +126,31 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
}
protected TimeZone getTimeZone(long deviceId, String defaultTimeZone) {
- TimeZone result = TimeZone.getTimeZone(defaultTimeZone);
String timeZoneName = AttributeUtil.lookup(cacheManager, Keys.DECODER_TIMEZONE, deviceId);
if (timeZoneName != null) {
- result = TimeZone.getTimeZone(timeZoneName);
+ return TimeZone.getTimeZone(timeZoneName);
+ } else if (defaultTimeZone != null) {
+ return TimeZone.getTimeZone(defaultTimeZone);
}
- return result;
+ return null;
}
public DeviceSession getDeviceSession(Channel channel, SocketAddress remoteAddress, String... uniqueIds) {
try {
return connectionManager.getDeviceSession(protocol, channel, remoteAddress, uniqueIds);
- } catch (StorageException e) {
+ } catch (Exception e) {
throw new RuntimeException(e);
}
}
+ public void setModelOverride(String modelOverride) {
+ this.modelOverride = modelOverride;
+ }
+
+ public String getDeviceModel(DeviceSession deviceSession) {
+ return modelOverride != null ? modelOverride : deviceSession.getModel();
+ }
+
public void getLastLocation(Position position, Date deviceTime) {
if (position.getDeviceId() != 0) {
position.setOutdated(true);
diff --git a/src/main/java/org/traccar/BaseProtocolEncoder.java b/src/main/java/org/traccar/BaseProtocolEncoder.java
index 9c3934184..e357c27dc 100644
--- a/src/main/java/org/traccar/BaseProtocolEncoder.java
+++ b/src/main/java/org/traccar/BaseProtocolEncoder.java
@@ -27,7 +27,7 @@ import org.traccar.model.Command;
import org.traccar.model.Device;
import org.traccar.session.cache.CacheManager;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public abstract class BaseProtocolEncoder extends ChannelOutboundHandlerAdapter {
@@ -39,6 +39,8 @@ public abstract class BaseProtocolEncoder extends ChannelOutboundHandlerAdapter
private CacheManager cacheManager;
+ private String modelOverride;
+
public BaseProtocolEncoder(Protocol protocol) {
this.protocol = protocol;
}
@@ -68,34 +70,42 @@ public abstract class BaseProtocolEncoder extends ChannelOutboundHandlerAdapter
}
}
- @Override
- public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
-
- NetworkMessage networkMessage = (NetworkMessage) msg;
+ public void setModelOverride(String modelOverride) {
+ this.modelOverride = modelOverride;
+ }
- if (networkMessage.getMessage() instanceof Command) {
+ public String getDeviceModel(long deviceId) {
+ String model = getCacheManager().getObject(Device.class, deviceId).getModel();
+ return modelOverride != null ? modelOverride : model;
+ }
- Command command = (Command) networkMessage.getMessage();
- Object encodedCommand = encodeCommand(ctx.channel(), command);
+ @Override
+ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
- 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());
+ if (msg instanceof NetworkMessage) {
+ NetworkMessage networkMessage = (NetworkMessage) msg;
+ if (networkMessage.getMessage() instanceof Command) {
- ctx.write(new NetworkMessage(encodedCommand, networkMessage.getRemoteAddress()), promise);
+ Command command = (Command) networkMessage.getMessage();
+ Object encodedCommand = encodeCommand(ctx.channel(), command);
- } else {
+ 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());
- 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/ExtendedObjectDecoder.java b/src/main/java/org/traccar/ExtendedObjectDecoder.java
index f79a36c85..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.
@@ -23,13 +23,15 @@ 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 javax.inject.Inject;
+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 {
@@ -68,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);
@@ -76,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/Main.java b/src/main/java/org/traccar/Main.java
index e34fbb72a..33fcf654f 100644
--- a/src/main/java/org/traccar/Main.java
+++ b/src/main/java/org/traccar/Main.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2024 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,10 +20,8 @@ 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;
@@ -33,10 +31,9 @@ import java.lang.management.MemoryMXBean;
import java.lang.management.OperatingSystemMXBean;
import java.lang.management.RuntimeMXBean;
import java.nio.charset.Charset;
+import java.util.ArrayList;
+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 {
@@ -70,8 +67,7 @@ public final class Main {
+ " heap: " + memoryBean.getHeapMemoryUsage().getMax() / (1024 * 1024) + "mb"
+ " non-heap: " + memoryBean.getNonHeapMemoryUsage().getMax() / (1024 * 1024) + "mb");
- LOGGER.info("Character encoding: "
- + System.getProperty("file.encoding") + " charset: " + Charset.defaultCharset());
+ LOGGER.info("Character encoding: " + Charset.defaultCharset().displayName());
} catch (Exception error) {
LOGGER.warn("Failed to get system info");
@@ -122,18 +118,14 @@ public final class Main {
LOGGER.info("Version: " + Main.class.getPackage().getImplementationVersion());
LOGGER.info("Starting server...");
- if (injector.getInstance(BroadcastService.class).singleInstance()) {
- DeviceUtil.resetStatus(injector.getInstance(Storage.class));
- }
-
- 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();
+ var services = new ArrayList<LifecycleObject>();
+ for (var clazz : List.of(
+ ScheduleManager.class, ServerManager.class, WebServer.class, BroadcastService.class)) {
+ var service = injector.getInstance(clazz);
+ if (service != null) {
+ service.start();
+ services.add(service);
+ }
}
Thread.setDefaultUncaughtExceptionHandler((t, e) -> LOGGER.error("Thread exception", e));
diff --git a/src/main/java/org/traccar/MainEventHandler.java b/src/main/java/org/traccar/MainEventHandler.java
index 877f03ae7..fb0171d63 100644
--- a/src/main/java/org/traccar/MainEventHandler.java
+++ b/src/main/java/org/traccar/MainEventHandler.java
@@ -27,6 +27,7 @@ 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;
@@ -40,8 +41,8 @@ import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Request;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
@@ -145,6 +146,8 @@ public class MainEventHandler extends ChannelInboundHandlerAdapter {
LOGGER.info(builder.toString());
statisticsManager.registerMessageStored(position.getDeviceId(), position.getProtocol());
+
+ ctx.writeAndFlush(new AcknowledgementHandler.EventHandled(position));
}
}
diff --git a/src/main/java/org/traccar/MainModule.java b/src/main/java/org/traccar/MainModule.java
index 55211d109..3fec4d1e6 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.
@@ -17,7 +17,7 @@ package org.traccar;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
-import com.fasterxml.jackson.datatype.jsr353.JSR353Module;
+import com.fasterxml.jackson.datatype.jsonp.JSONPModule;
import com.google.inject.AbstractModule;
import com.google.inject.Injector;
import com.google.inject.Provides;
@@ -26,22 +26,25 @@ import com.google.inject.name.Names;
import io.netty.util.HashedWheelTimer;
import io.netty.util.Timer;
import org.apache.velocity.app.VelocityEngine;
-import org.apache.velocity.runtime.log.NullLogChute;
-import org.eclipse.jetty.util.URIUtil;
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.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;
@@ -74,6 +77,7 @@ import org.traccar.handler.GeolocationHandler;
import org.traccar.handler.SpeedLimitHandler;
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;
@@ -84,16 +88,18 @@ 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 javax.annotation.Nullable;
-import javax.inject.Singleton;
-import javax.ws.rs.client.Client;
-import javax.ws.rs.client.ClientBuilder;
+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.InetAddress;
-import java.net.UnknownHostException;
+import java.net.URISyntaxException;
+import java.net.http.HttpClient;
import java.util.Properties;
public class MainModule extends AbstractModule {
@@ -108,18 +114,27 @@ public class MainModule extends AbstractModule {
protected void configure() {
bindConstant().annotatedWith(Names.named("configFile")).to(configFile);
bind(Config.class).asEagerSingleton();
- bind(Storage.class).to(DatabaseStorage.class).in(Scopes.SINGLETON);
bind(Timer.class).to(HashedWheelTimer.class).in(Scopes.SINGLETON);
}
@Singleton
@Provides
+ 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 ObjectMapper provideObjectMapper(Config config) {
ObjectMapper objectMapper = new ObjectMapper();
if (config.getBoolean(Keys.WEB_SANITIZE)) {
objectMapper.registerModule(new SanitizerModule());
}
- objectMapper.registerModule(new JSR353Module());
+ objectMapper.registerModule(new JSONPModule());
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
return objectMapper;
}
@@ -160,6 +175,17 @@ public class MainModule extends AbstractModule {
return null;
}
+ @Singleton
+ @Provides
+ 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;
+ }
+
@Provides
public static WebServer provideWebServer(Injector injector, Config config) {
if (config.hasKey(Keys.WEB_PORT)) {
@@ -174,7 +200,6 @@ public class MainModule extends AbstractModule {
if (config.getBoolean(Keys.GEOCODER_ENABLE)) {
String type = config.getString(Keys.GEOCODER_TYPE, "google");
String url = config.getString(Keys.GEOCODER_URL);
- String id = config.getString(Keys.GEOCODER_ID);
String key = config.getString(Keys.GEOCODER_KEY);
String language = config.getString(Keys.GEOCODER_LANGUAGE);
String formatString = config.getString(Keys.GEOCODER_FORMAT);
@@ -217,7 +242,7 @@ public class MainModule extends AbstractModule {
geocoder = new BanGeocoder(client, cacheSize, addressFormat);
break;
case "here":
- geocoder = new HereGeocoder(client, url, id, key, language, cacheSize, addressFormat);
+ geocoder = new HereGeocoder(client, url, key, language, cacheSize, addressFormat);
break;
case "mapmyindia":
geocoder = new MapmyIndiaGeocoder(client, url, key, cacheSize, addressFormat);
@@ -277,7 +302,7 @@ public class MainModule extends AbstractModule {
switch (type) {
case "overpass":
default:
- return new OverpassSpeedLimitProvider(client, url);
+ return new OverpassSpeedLimitProvider(config, client, url);
}
}
return null;
@@ -317,8 +342,15 @@ public class MainModule extends AbstractModule {
@Provides
public static BroadcastService provideBroadcastService(
Config config, ObjectMapper objectMapper) throws IOException {
- if (config.hasKey(Keys.BROADCAST_ADDRESS)) {
- return new MulticastBroadcastService(config, objectMapper);
+ 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 new NullBroadcastService();
}
@@ -329,10 +361,13 @@ public class MainModule extends AbstractModule {
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);
}
@@ -347,8 +382,13 @@ public class MainModule extends AbstractModule {
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);
}
@@ -360,21 +400,8 @@ public class MainModule extends AbstractModule {
@Provides
public static VelocityEngine provideVelocityEngine(Config config) {
Properties properties = new Properties();
- properties.setProperty("file.resource.loader.path", config.getString(Keys.TEMPLATES_ROOT) + "/");
- properties.setProperty("runtime.log.logsystem.class", NullLogChute.class.getName());
-
- if (config.hasKey(Keys.WEB_URL)) {
- properties.setProperty("web.url", config.getString(Keys.WEB_URL).replaceAll("/$", ""));
- } else {
- String address;
- try {
- address = config.getString(Keys.WEB_ADDRESS, InetAddress.getLocalHost().getHostAddress());
- } catch (UnknownHostException e) {
- address = "localhost";
- }
- String url = URIUtil.newURI("http", address, config.getInteger(Keys.WEB_PORT), "", "");
- properties.setProperty("web.url", url);
- }
+ properties.setProperty("resource.loader.file.path", config.getString(Keys.TEMPLATES_ROOT) + "/");
+ properties.setProperty("web.url", WebHelper.retrieveWebUrl(config));
VelocityEngine velocityEngine = new VelocityEngine();
velocityEngine.init(properties);
diff --git a/src/main/java/org/traccar/PositionForwardingHandler.java b/src/main/java/org/traccar/PositionForwardingHandler.java
index 83f91e937..a79b01367 100644
--- a/src/main/java/org/traccar/PositionForwardingHandler.java
+++ b/src/main/java/org/traccar/PositionForwardingHandler.java
@@ -30,9 +30,9 @@ import org.traccar.model.Device;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;
-import javax.annotation.Nullable;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.annotation.Nullable;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
diff --git a/src/main/java/org/traccar/ServerManager.java b/src/main/java/org/traccar/ServerManager.java
index 57afb01fd..e91be50a2 100644
--- a/src/main/java/org/traccar/ServerManager.java
+++ b/src/main/java/org/traccar/ServerManager.java
@@ -22,8 +22,8 @@ import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.helper.ClassScanner;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
import java.io.IOException;
import java.net.BindException;
import java.net.ConnectException;
diff --git a/src/main/java/org/traccar/WindowsService.java b/src/main/java/org/traccar/WindowsService.java
index f233337a7..08eba25a6 100644
--- a/src/main/java/org/traccar/WindowsService.java
+++ b/src/main/java/org/traccar/WindowsService.java
@@ -170,7 +170,7 @@ public abstract class WindowsService {
public abstract void run();
- private class ServiceMain implements SERVICE_MAIN_FUNCTION {
+ private final class ServiceMain implements SERVICE_MAIN_FUNCTION {
public void callback(int dwArgc, Pointer lpszArgv) {
ServiceControl serviceControl = new ServiceControl();
@@ -203,7 +203,7 @@ public abstract class WindowsService {
}
- private class ServiceControl implements HandlerEx {
+ private final class ServiceControl implements HandlerEx {
public int callback(int dwControl, int dwEventType, Pointer lpEventData, Pointer lpContext) {
switch (dwControl) {
diff --git a/src/main/java/org/traccar/api/AsyncSocket.java b/src/main/java/org/traccar/api/AsyncSocket.java
index 5fc4b4412..f5fbcbf62 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 - 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.
@@ -22,16 +22,17 @@ import org.eclipse.jetty.websocket.api.WebSocketAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.LogRecord;
import org.traccar.model.Position;
+import org.traccar.session.ConnectionManager;
import org.traccar.storage.Storage;
import org.traccar.storage.StorageException;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.UpdateListener {
@@ -41,12 +42,15 @@ public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.U
private static final String KEY_DEVICES = "devices";
private static final String KEY_POSITIONS = "positions";
private static final String KEY_EVENTS = "events";
+ private static final String KEY_LOGS = "logs";
private final ObjectMapper objectMapper;
private final ConnectionManager connectionManager;
private final Storage storage;
private final long userId;
+ private boolean includeLogs;
+
public AsyncSocket(ObjectMapper objectMapper, ConnectionManager connectionManager, Storage storage, long userId) {
this.objectMapper = objectMapper;
this.connectionManager = connectionManager;
@@ -76,29 +80,41 @@ public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.U
}
@Override
+ public void onWebSocketText(String message) {
+ super.onWebSocketText(message);
+
+ try {
+ includeLogs = objectMapper.readTree(message).get("logs").asBoolean();
+ } catch (JsonProcessingException e) {
+ LOGGER.warn("Socket JSON parsing error", e);
+ }
+ }
+
+ @Override
public void onKeepalive() {
sendData(new HashMap<>());
}
@Override
public void onUpdateDevice(Device device) {
- Map<String, Collection<?>> data = new HashMap<>();
- data.put(KEY_DEVICES, Collections.singletonList(device));
- sendData(data);
+ sendData(Map.of(KEY_DEVICES, List.of(device)));
}
@Override
public void onUpdatePosition(Position position) {
- Map<String, Collection<?>> data = new HashMap<>();
- data.put(KEY_POSITIONS, Collections.singletonList(position));
- sendData(data);
+ sendData(Map.of(KEY_POSITIONS, List.of(position)));
}
@Override
public void onUpdateEvent(Event event) {
- Map<String, Collection<?>> data = new HashMap<>();
- data.put(KEY_EVENTS, Collections.singletonList(event));
- sendData(data);
+ sendData(Map.of(KEY_EVENTS, List.of(event)));
+ }
+
+ @Override
+ public void onUpdateLog(LogRecord record) {
+ if (includeLogs) {
+ sendData(Map.of(KEY_LOGS, List.of(record)));
+ }
}
private void sendData(Map<String, Collection<?>> data) {
diff --git a/src/main/java/org/traccar/api/AsyncSocketServlet.java b/src/main/java/org/traccar/api/AsyncSocketServlet.java
index 91a745eeb..cd2c1639e 100644
--- a/src/main/java/org/traccar/api/AsyncSocketServlet.java
+++ b/src/main/java/org/traccar/api/AsyncSocketServlet.java
@@ -24,9 +24,9 @@ import org.traccar.config.Keys;
import org.traccar.session.ConnectionManager;
import org.traccar.storage.Storage;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-import javax.servlet.http.HttpSession;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+import jakarta.servlet.http.HttpSession;
import java.time.Duration;
@Singleton
diff --git a/src/main/java/org/traccar/api/BaseObjectResource.java b/src/main/java/org/traccar/api/BaseObjectResource.java
index 904781e54..2a801221b 100644
--- a/src/main/java/org/traccar/api/BaseObjectResource.java
+++ b/src/main/java/org/traccar/api/BaseObjectResource.java
@@ -16,6 +16,8 @@
*/
package org.traccar.api;
+import org.traccar.api.security.ServiceAccountUser;
+import org.traccar.model.ObjectOperation;
import org.traccar.helper.LogAction;
import org.traccar.model.BaseModel;
import org.traccar.model.Group;
@@ -28,14 +30,14 @@ import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Request;
-import javax.inject.Inject;
-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 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 {
@@ -65,22 +67,25 @@ public abstract class BaseObjectResource<T extends BaseModel> extends BaseResour
}
@POST
- public Response add(T entity) throws StorageException {
+ public Response add(T entity) throws Exception {
permissionsService.checkEdit(getUserId(), entity, true);
entity.setId(storage.addObject(entity, new Request(new Columns.Exclude("id"))));
LogAction.create(getUserId(), entity);
- 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());
+
+ if (getUserId() != ServiceAccountUser.ID) {
+ storage.addPermission(new Permission(User.class, getUserId(), baseClass, entity.getId()));
+ cacheManager.invalidatePermission(true, User.class, getUserId(), baseClass, entity.getId(), true);
+ connectionManager.invalidatePermission(true, User.class, getUserId(), baseClass, entity.getId(), true);
+ LogAction.link(getUserId(), User.class, getUserId(), baseClass, entity.getId());
+ }
return Response.ok(entity).build();
}
@Path("{id}")
@PUT
- public Response update(T entity) throws StorageException {
+ public Response update(T entity) throws Exception {
permissionsService.checkEdit(getUserId(), entity, false);
permissionsService.checkPermission(baseClass, getUserId(), entity.getId());
@@ -106,7 +111,7 @@ public abstract class BaseObjectResource<T extends BaseModel> extends BaseResour
new Condition.Equals("id", entity.getId())));
}
}
- cacheManager.updateOrInvalidate(true, entity);
+ cacheManager.invalidateObject(true, entity.getClass(), entity.getId(), ObjectOperation.UPDATE);
LogAction.edit(getUserId(), entity);
return Response.ok(entity).build();
@@ -114,12 +119,12 @@ public abstract class BaseObjectResource<T extends BaseModel> extends BaseResour
@Path("{id}")
@DELETE
- public Response remove(@PathParam("id") long id) throws StorageException {
+ public Response remove(@PathParam("id") long id) throws Exception {
permissionsService.checkEdit(getUserId(), baseClass, false);
permissionsService.checkPermission(baseClass, getUserId(), id);
storage.removeObject(baseClass, new Request(new Condition.Equals("id", id)));
- cacheManager.invalidate(baseClass, id);
+ cacheManager.invalidateObject(true, baseClass, id, ObjectOperation.DELETE);
LogAction.remove(getUserId(), baseClass, id);
diff --git a/src/main/java/org/traccar/api/BaseResource.java b/src/main/java/org/traccar/api/BaseResource.java
index 33abe73fa..f20b8c4c2 100644
--- a/src/main/java/org/traccar/api/BaseResource.java
+++ b/src/main/java/org/traccar/api/BaseResource.java
@@ -19,9 +19,9 @@ import org.traccar.api.security.PermissionsService;
import org.traccar.api.security.UserPrincipal;
import org.traccar.storage.Storage;
-import javax.inject.Inject;
-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 {
diff --git a/src/main/java/org/traccar/api/CorsResponseFilter.java b/src/main/java/org/traccar/api/CorsResponseFilter.java
index 67d0341a1..a380eb41d 100644
--- a/src/main/java/org/traccar/api/CorsResponseFilter.java
+++ b/src/main/java/org/traccar/api/CorsResponseFilter.java
@@ -19,11 +19,11 @@ import io.netty.handler.codec.http.HttpHeaderNames;
import org.traccar.config.Config;
import org.traccar.config.Keys;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-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
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 8467b46c6..348ebe38a 100644
--- a/src/main/java/org/traccar/api/ExtendedObjectResource.java
+++ b/src/main/java/org/traccar/api/ExtendedObjectResource.java
@@ -25,8 +25,8 @@ import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Request;
-import javax.ws.rs.GET;
-import javax.ws.rs.QueryParam;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.QueryParam;
import java.util.Collection;
import java.util.LinkedList;
diff --git a/src/main/java/org/traccar/api/MediaFilter.java b/src/main/java/org/traccar/api/MediaFilter.java
index ab75bdc5d..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 - 2022 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");
@@ -28,17 +28,16 @@ import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Request;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-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 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
@@ -58,10 +57,6 @@ public class MediaFilter implements Filter {
}
@Override
- public void init(FilterConfig filterConfig) throws ServletException {
- }
-
- @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
@@ -99,8 +94,4 @@ public class MediaFilter implements Filter {
}
}
- @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 4a435ca7d..c9d41b063 100644
--- a/src/main/java/org/traccar/api/SimpleObjectResource.java
+++ b/src/main/java/org/traccar/api/SimpleObjectResource.java
@@ -23,8 +23,8 @@ import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Request;
-import javax.ws.rs.GET;
-import javax.ws.rs.QueryParam;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.QueryParam;
import java.util.Collection;
import java.util.LinkedList;
diff --git a/src/main/java/org/traccar/api/resource/AttributeResource.java b/src/main/java/org/traccar/api/resource/AttributeResource.java
index f85e90133..52c4d6324 100644
--- a/src/main/java/org/traccar/api/resource/AttributeResource.java
+++ b/src/main/java/org/traccar/api/resource/AttributeResource.java
@@ -16,17 +16,17 @@
*/
package org.traccar.api.resource;
-import javax.inject.Inject;
-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.api.ExtendedObjectResource;
import org.traccar.model.Attribute;
@@ -78,21 +78,21 @@ public class AttributeResource extends ExtendedObjectResource<Attribute> {
}
@POST
- public Response add(Attribute entity) throws StorageException {
+ public Response add(Attribute entity) throws Exception {
permissionsService.checkAdmin(getUserId());
return super.add(entity);
}
@Path("{id}")
@PUT
- public Response update(Attribute entity) throws StorageException {
+ public Response update(Attribute entity) throws Exception {
permissionsService.checkAdmin(getUserId());
return super.update(entity);
}
@Path("{id}")
@DELETE
- public Response remove(@PathParam("id") long id) throws StorageException {
+ public Response remove(@PathParam("id") long id) throws Exception {
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 3460cf6e0..c23d91e77 100644
--- a/src/main/java/org/traccar/api/resource/CommandResource.java
+++ b/src/main/java/org/traccar/api/resource/CommandResource.java
@@ -23,9 +23,12 @@ 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;
@@ -34,15 +37,15 @@ import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Request;
-import javax.inject.Inject;
-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 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;
@@ -104,7 +107,7 @@ public class CommandResource extends ExtendedObjectResource<Command> {
@POST
@Path("send")
- public Response send(Command entity) throws Exception {
+ 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();
@@ -114,9 +117,28 @@ public class CommandResource extends ExtendedObjectResource<Command> {
} else {
permissionsService.checkRestriction(getUserId(), UserRestrictions::getLimitCommands);
}
- permissionsService.checkPermission(Device.class, getUserId(), entity.getDeviceId());
- if (!commandsManager.sendCommand(entity)) {
- return Response.accepted(entity).build();
+
+ if (groupId > 0) {
+ permissionsService.checkPermission(Group.class, getUserId(), groupId);
+ var devices = DeviceUtil.getAccessibleDevices(storage, getUserId(), List.of(), List.of(groupId));
+ List<QueuedCommand> queuedCommands = new ArrayList<>();
+ for (Device device : devices) {
+ Command command = QueuedCommand.fromCommand(entity).toCommand();
+ command.setDeviceId(device.getId());
+ QueuedCommand queuedCommand = commandsManager.sendCommand(command);
+ if (queuedCommand != null) {
+ queuedCommands.add(queuedCommand);
+ }
+ }
+ if (!queuedCommands.isEmpty()) {
+ return Response.accepted(queuedCommands).build();
+ }
+ } else {
+ permissionsService.checkPermission(Device.class, getUserId(), entity.getDeviceId());
+ QueuedCommand queuedCommand = commandsManager.sendCommand(entity);
+ if (queuedCommand != null) {
+ return Response.accepted(queuedCommand).build();
+ }
}
return Response.ok(entity).build();
}
diff --git a/src/main/java/org/traccar/api/resource/DeviceResource.java b/src/main/java/org/traccar/api/resource/DeviceResource.java
index c0b0cea0d..89bba7237 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 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2024 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,12 +15,17 @@
*/
package org.traccar.api.resource;
+import jakarta.ws.rs.FormParam;
import org.traccar.api.BaseObjectResource;
+import org.traccar.api.signature.TokenManager;
import org.traccar.broadcast.BroadcastService;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
import org.traccar.database.MediaManager;
import org.traccar.helper.LogAction;
import org.traccar.model.Device;
import org.traccar.model.DeviceAccumulators;
+import org.traccar.model.Permission;
import org.traccar.model.Position;
import org.traccar.model.User;
import org.traccar.session.ConnectionManager;
@@ -30,23 +35,25 @@ import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Request;
-import javax.inject.Inject;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.GET;
-import javax.ws.rs.HeaderParam;
-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.HttpHeaders;
-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.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.security.GeneralSecurityException;
import java.util.Collection;
+import java.util.Date;
import java.util.LinkedList;
import java.util.List;
@@ -56,6 +63,9 @@ import java.util.List;
public class DeviceResource extends BaseObjectResource<Device> {
@Inject
+ private Config config;
+
+ @Inject
private CacheManager cacheManager;
@Inject
@@ -67,6 +77,9 @@ public class DeviceResource extends BaseObjectResource<Device> {
@Inject
private MediaManager mediaManager;
+ @Inject
+ private TokenManager tokenManager;
+
public DeviceResource() {
super(Device.class);
}
@@ -120,7 +133,7 @@ public class DeviceResource extends BaseObjectResource<Device> {
@Path("{id}/accumulators")
@PUT
- public Response updateAccumulators(DeviceAccumulators entity) throws StorageException {
+ public Response updateAccumulators(DeviceAccumulators entity) throws Exception {
if (permissionsService.notAdmin(getUserId())) {
permissionsService.checkManager(getUserId());
permissionsService.checkPermission(Device.class, getUserId(), entity.getDeviceId());
@@ -183,4 +196,50 @@ public class DeviceResource extends BaseObjectResource<Device> {
return Response.status(Response.Status.NOT_FOUND).build();
}
+ @Path("share")
+ @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+ @POST
+ public String shareDevice(
+ @FormParam("deviceId") long deviceId,
+ @FormParam("expiration") Date expiration) throws StorageException, GeneralSecurityException, IOException {
+
+ User user = permissionsService.getUser(getUserId());
+ if (permissionsService.getServer().getBoolean(Keys.DEVICE_SHARE_DISABLE.getKey())) {
+ throw new SecurityException("Sharing is disabled");
+ }
+ if (user.getTemporary()) {
+ throw new SecurityException("Temporary user");
+ }
+ if (user.getExpirationTime() != null && user.getExpirationTime().before(expiration)) {
+ expiration = user.getExpirationTime();
+ }
+
+ Device device = storage.getObject(Device.class, new Request(
+ new Columns.All(),
+ new Condition.And(
+ new Condition.Equals("id", deviceId),
+ new Condition.Permission(User.class, user.getId(), Device.class))));
+
+ String shareEmail = user.getEmail() + ":" + device.getUniqueId();
+ User share = storage.getObject(User.class, new Request(
+ new Columns.All(), new Condition.Equals("email", shareEmail)));
+
+ if (share == null) {
+ share = new User();
+ share.setName(device.getName());
+ share.setEmail(shareEmail);
+ share.setExpirationTime(expiration);
+ share.setTemporary(true);
+ share.setReadonly(true);
+ share.setLimitCommands(user.getLimitCommands() || !config.getBoolean(Keys.WEB_SHARE_DEVICE_COMMANDS));
+ share.setDisableReports(user.getDisableReports() || !config.getBoolean(Keys.WEB_SHARE_DEVICE_REPORTS));
+
+ share.setId(storage.addObject(share, new Request(new Columns.Exclude("id"))));
+
+ storage.addPermission(new Permission(User.class, share.getId(), Device.class, deviceId));
+ }
+
+ return tokenManager.generateToken(share.getId(), expiration);
+ }
+
}
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 afdaf52b5..1f20b880d 100644
--- a/src/main/java/org/traccar/api/resource/EventResource.java
+++ b/src/main/java/org/traccar/api/resource/EventResource.java
@@ -23,14 +23,14 @@ 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.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
+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)
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 2e4ad12f3..43dc1fa98 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 - 2022 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,6 +15,16 @@
*/
package org.traccar.api.resource;
+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.QueryParam;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.api.ExtendedObjectResource;
@@ -23,20 +33,16 @@ import org.traccar.model.Notification;
import org.traccar.model.Typed;
import org.traccar.model.User;
import org.traccar.notification.MessageException;
+import org.traccar.notification.NotificationMessage;
import org.traccar.notification.NotificatorManager;
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.inject.Inject;
-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 java.lang.reflect.Field;
import java.lang.reflect.Modifier;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
@@ -80,10 +86,10 @@ public class NotificationResource extends ExtendedObjectResource<Notification> {
@POST
@Path("test")
- public Response testMessage() throws MessageException, InterruptedException, StorageException {
+ public Response testMessage() throws MessageException, StorageException {
User user = permissionsService.getUser(getUserId());
for (Typed method : notificatorManager.getAllNotificatorTypes()) {
- notificatorManager.getNotificator(method.getType()).send(user, new Event("test", 0), null);
+ notificatorManager.getNotificator(method.getType()).send(null, user, new Event("test", 0), null);
}
return Response.noContent().build();
}
@@ -91,9 +97,31 @@ public class NotificationResource extends ExtendedObjectResource<Notification> {
@POST
@Path("test/{notificator}")
public Response testMessage(@PathParam("notificator") String notificator)
- throws MessageException, InterruptedException, StorageException {
+ throws MessageException, StorageException {
User user = permissionsService.getUser(getUserId());
- notificatorManager.getNotificator(notificator).send(user, new Event("test", 0), null);
+ notificatorManager.getNotificator(notificator).send(null, user, new Event("test", 0), null);
+ return Response.noContent().build();
+ }
+
+ @POST
+ @Path("send/{notificator}")
+ public Response sendMessage(
+ @PathParam("notificator") String notificator, @QueryParam("userId") List<Long> userIds,
+ NotificationMessage message) throws MessageException, StorageException {
+ permissionsService.checkAdmin(getUserId());
+ List<User> users;
+ if (userIds.isEmpty()) {
+ users = storage.getObjects(User.class, new Request(new Columns.All()));
+ } else {
+ users = new ArrayList<>();
+ for (long userId : userIds) {
+ users.add(storage.getObject(
+ User.class, new Request(new Columns.All(), new Condition.Equals("id", userId))));
+ }
+ }
+ for (User user : users) {
+ notificatorManager.getNotificator(notificator).send(user, message, null, 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 2d87a8665..22b3f0cd3 100644
--- a/src/main/java/org/traccar/api/resource/PasswordResource.java
+++ b/src/main/java/org/traccar/api/resource/PasswordResource.java
@@ -25,16 +25,16 @@ 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.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 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;
@@ -63,7 +63,7 @@ public class PasswordResource extends BaseResource {
if (user != null) {
var velocityContext = textTemplateFormatter.prepareContext(permissionsService.getServer(), user);
var fullMessage = textTemplateFormatter.formatMessage(velocityContext, "passwordReset", "full");
- mailManager.sendMessage(user, fullMessage.getSubject(), fullMessage.getBody());
+ mailManager.sendMessage(user, true, fullMessage.getSubject(), fullMessage.getBody());
}
return Response.ok().build();
}
@@ -75,7 +75,7 @@ public class PasswordResource extends BaseResource {
@FormParam("token") String token, @FormParam("password") String password)
throws StorageException, GeneralSecurityException, IOException {
- long userId = tokenManager.verifyToken(token);
+ long userId = tokenManager.verifyToken(token).getUserId();
User user = storage.getObject(User.class, new Request(
new Columns.All(), new Condition.Equals("id", userId)));
if (user != null) {
diff --git a/src/main/java/org/traccar/api/resource/PermissionsResource.java b/src/main/java/org/traccar/api/resource/PermissionsResource.java
index d35cb98bb..9e2d21f2c 100644
--- a/src/main/java/org/traccar/api/resource/PermissionsResource.java
+++ b/src/main/java/org/traccar/api/resource/PermissionsResource.java
@@ -23,15 +23,15 @@ import org.traccar.model.UserRestrictions;
import org.traccar.session.cache.CacheManager;
import org.traccar.storage.StorageException;
-import javax.inject.Inject;
-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 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;
@@ -48,7 +48,7 @@ public class PermissionsResource extends BaseResource {
private void checkPermission(Permission permission) throws StorageException {
if (permissionsService.notAdmin(getUserId())) {
permissionsService.checkPermission(permission.getOwnerClass(), getUserId(), permission.getOwnerId());
- permissionsService.checkPermission(permission.getOwnerClass(), getUserId(), permission.getOwnerId());
+ permissionsService.checkPermission(permission.getPropertyClass(), getUserId(), permission.getPropertyId());
}
}
@@ -64,7 +64,7 @@ public class PermissionsResource extends BaseResource {
@Path("bulk")
@POST
- public Response add(List<LinkedHashMap<String, Long>> entities) throws StorageException, ClassNotFoundException {
+ public Response add(List<LinkedHashMap<String, Long>> entities) throws Exception {
permissionsService.checkRestriction(getUserId(), UserRestrictions::getReadonly);
checkPermissionTypes(entities);
for (LinkedHashMap<String, Long> entity: entities) {
@@ -74,7 +74,8 @@ public class PermissionsResource extends BaseResource {
cacheManager.invalidatePermission(
true,
permission.getOwnerClass(), permission.getOwnerId(),
- permission.getPropertyClass(), permission.getPropertyId());
+ permission.getPropertyClass(), permission.getPropertyId(),
+ true);
LogAction.link(getUserId(),
permission.getOwnerClass(), permission.getOwnerId(),
permission.getPropertyClass(), permission.getPropertyId());
@@ -83,13 +84,13 @@ public class PermissionsResource extends BaseResource {
}
@POST
- public Response add(LinkedHashMap<String, Long> entity) throws StorageException, ClassNotFoundException {
+ public Response add(LinkedHashMap<String, Long> entity) throws Exception {
return add(Collections.singletonList(entity));
}
@DELETE
@Path("bulk")
- public Response remove(List<LinkedHashMap<String, Long>> entities) throws StorageException, ClassNotFoundException {
+ public Response remove(List<LinkedHashMap<String, Long>> entities) throws Exception {
permissionsService.checkRestriction(getUserId(), UserRestrictions::getReadonly);
checkPermissionTypes(entities);
for (LinkedHashMap<String, Long> entity: entities) {
@@ -99,7 +100,8 @@ public class PermissionsResource extends BaseResource {
cacheManager.invalidatePermission(
true,
permission.getOwnerClass(), permission.getOwnerId(),
- permission.getPropertyClass(), permission.getPropertyId());
+ permission.getPropertyClass(), permission.getPropertyId(),
+ false);
LogAction.unlink(getUserId(),
permission.getOwnerClass(), permission.getOwnerId(),
permission.getPropertyClass(), permission.getPropertyId());
@@ -108,7 +110,7 @@ public class PermissionsResource extends BaseResource {
}
@DELETE
- public Response remove(LinkedHashMap<String, Long> entity) throws StorageException, ClassNotFoundException {
+ public Response remove(LinkedHashMap<String, Long> entity) throws Exception {
return remove(Collections.singletonList(entity));
}
diff --git a/src/main/java/org/traccar/api/resource/PositionResource.java b/src/main/java/org/traccar/api/resource/PositionResource.java
index 042dd1e23..0d783a0fe 100644
--- a/src/main/java/org/traccar/api/resource/PositionResource.java
+++ b/src/main/java/org/traccar/api/resource/PositionResource.java
@@ -28,21 +28,23 @@ import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Request;
-import javax.inject.Inject;
-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.WebApplicationException;
-import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.StreamingOutput;
+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.Date;
import java.util.List;
+import java.util.LinkedList;
@Path("positions")
@Produces(MediaType.APPLICATION_JSON)
@@ -86,6 +88,21 @@ public class PositionResource extends BaseResource {
}
}
+ @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")
diff --git a/src/main/java/org/traccar/api/resource/ReportResource.java b/src/main/java/org/traccar/api/resource/ReportResource.java
index 6944de9cb..55a96fa90 100644
--- a/src/main/java/org/traccar/api/resource/ReportResource.java
+++ b/src/main/java/org/traccar/api/resource/ReportResource.java
@@ -16,13 +16,14 @@
*/
package org.traccar.api.resource;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-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.model.Report;
import org.traccar.model.UserRestrictions;
+import org.traccar.reports.CombinedReportProvider;
+import org.traccar.reports.DevicesReportProvider;
import org.traccar.reports.EventsReportProvider;
import org.traccar.reports.RouteReportProvider;
import org.traccar.reports.StopsReportProvider;
@@ -30,23 +31,24 @@ 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 javax.inject.Inject;
-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.QueryParam;
-import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.StreamingOutput;
+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;
@@ -54,13 +56,14 @@ import java.util.List;
@Path("reports")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
-public class ReportResource extends BaseResource {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(ReportResource.class);
+public class ReportResource extends SimpleObjectResource<Report> {
private static final String EXCEL = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
@Inject
+ private CombinedReportProvider combinedReportProvider;
+
+ @Inject
private EventsReportProvider eventsReportProvider;
@Inject
@@ -76,8 +79,15 @@ public class ReportResource extends BaseResource {
private TripsReportProvider tripsReportProvider;
@Inject
+ private DevicesReportProvider devicesReportProvider;
+
+ @Inject
private ReportMailer reportMailer;
+ public ReportResource() {
+ super(Report.class);
+ }
+
private Response executeReport(long userId, boolean mail, ReportExecutor executor) {
if (mail) {
reportMailer.sendAsync(userId, executor);
@@ -95,6 +105,18 @@ public class ReportResource extends BaseResource {
}
}
+ @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(
@@ -301,4 +323,15 @@ public class ReportResource extends BaseResource {
return getStopsExcel(deviceIds, groupIds, from, to, type.equals("mail"));
}
+ @Path("devices/{type:xlsx|mail}")
+ @GET
+ @Produces(EXCEL)
+ public Response geDevicesExcel(
+ @PathParam("type") String type) throws StorageException {
+ permissionsService.checkRestriction(getUserId(), UserRestrictions::getDisableReports);
+ return executeReport(getUserId(), type.equals("mail"), stream -> {
+ devicesReportProvider.getExcel(stream, getUserId());
+ });
+ }
+
}
diff --git a/src/main/java/org/traccar/api/resource/ServerResource.java b/src/main/java/org/traccar/api/resource/ServerResource.java
index 4b7ee9189..2a72d2773 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.
@@ -16,30 +16,43 @@
package org.traccar.api.resource;
import org.traccar.api.BaseResource;
-import org.traccar.helper.model.UserUtil;
-import org.traccar.mail.MailManager;
+import org.traccar.model.ObjectOperation;
+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.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.Nullable;
-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;
@@ -50,6 +63,9 @@ import java.util.TimeZone;
public class ServerResource extends BaseResource {
@Inject
+ private Config config;
+
+ @Inject
private CacheManager cacheManager;
@Inject
@@ -57,6 +73,14 @@ public class ServerResource extends BaseResource {
@Inject
@Nullable
+ private SmsManager smsManager;
+
+ @Inject
+ @Nullable
+ private OpenIdProvider openIdProvider;
+
+ @Inject
+ @Nullable
private Geocoder geocoder;
@PermitAll
@@ -64,7 +88,10 @@ public class ServerResource extends BaseResource {
public Server get() throws StorageException {
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()) {
@@ -73,21 +100,18 @@ public class ServerResource extends BaseResource {
} 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 {
+ public Response update(Server server) throws Exception {
permissionsService.checkAdmin(getUserId());
- storage.updateObject(entity, new Request(
+ storage.updateObject(server, 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();
+ new Condition.Equals("id", server.getId())));
+ cacheManager.invalidateObject(true, Server.class, server.getId(), ObjectOperation.UPDATE);
+ LogAction.edit(getUserId(), server);
+ return Response.ok(server).build();
}
@Path("geocode")
@@ -106,4 +130,35 @@ public class ServerResource extends BaseResource {
return Arrays.asList(TimeZone.getAvailableIDs());
}
+ @Path("file/{path}")
+ @POST
+ @Consumes("*/*")
+ public Response uploadFile(@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 rootPath = Paths.get(root).normalize();
+ var outputPath = rootPath.resolve(path).normalize();
+ if (!outputPath.startsWith(rootPath)) {
+ return Response.status(Response.Status.BAD_REQUEST).build();
+ }
+
+ 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();
+ }
+
+ @Path("cache")
+ @GET
+ public String cache() throws StorageException {
+ permissionsService.checkAdmin(getUserId());
+ return cacheManager.toString();
+ }
+
}
diff --git a/src/main/java/org/traccar/api/resource/SessionResource.java b/src/main/java/org/traccar/api/resource/SessionResource.java
index 7025d5fa7..979b62589 100644
--- a/src/main/java/org/traccar/api/resource/SessionResource.java
+++ b/src/main/java/org/traccar/api/resource/SessionResource.java
@@ -16,39 +16,41 @@
package org.traccar.api.resource;
import org.traccar.api.BaseResource;
+import org.traccar.api.security.CodeRequiredException;
+import org.traccar.api.security.LoginResult;
import org.traccar.api.security.LoginService;
import org.traccar.api.signature.TokenManager;
-import org.traccar.helper.DataConverter;
+import org.traccar.database.OpenIdProvider;
import org.traccar.helper.LogAction;
-import org.traccar.helper.ServletHelper;
+import org.traccar.helper.WebHelper;
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.inject.Inject;
-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.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
-import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
+import com.nimbusds.oauth2.sdk.ParseException;
+import jakarta.annotation.Nullable;
+import jakarta.annotation.security.PermitAll;
+import jakarta.inject.Inject;
+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)
@@ -56,13 +58,16 @@ import java.util.Date;
public class SessionResource extends BaseResource {
public static final String USER_ID_KEY = "userId";
- public static final String USER_COOKIE_KEY = "user";
- public static final String PASS_COOKIE_KEY = "password";
+ public static final String EXPIRATION_KEY = "expiration";
@Inject
private LoginService loginService;
@Inject
+ @Nullable
+ private OpenIdProvider openIdProvider;
+
+ @Inject
private TokenManager tokenManager;
@Context
@@ -73,48 +78,22 @@ public class SessionResource extends BaseResource {
public User get(@QueryParam("token") String token) throws StorageException, IOException, GeneralSecurityException {
if (token != null) {
- User user = loginService.login(token);
- if (user != null) {
+ LoginResult loginResult = loginService.login(token);
+ if (loginResult != null) {
+ User user = loginResult.getUser();
request.getSession().setAttribute(USER_ID_KEY, user.getId());
- LogAction.login(user.getId(), ServletHelper.retrieveRemoteAddress(request));
+ request.getSession().setAttribute(EXPIRATION_KEY, loginResult.getExpiration());
+ LogAction.login(user.getId(), WebHelper.retrieveRemoteAddress(request));
return user;
}
}
Long userId = (Long) request.getSession().getAttribute(USER_ID_KEY);
- if (userId == null) {
-
- Cookie[] cookies = request.getCookies();
- String email = null, password = null;
- if (cookies != null) {
- for (Cookie cookie : cookies) {
- if (cookie.getName().equals(USER_COOKIE_KEY)) {
- byte[] emailBytes = DataConverter.parseBase64(
- 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));
- password = new String(passwordBytes, StandardCharsets.UTF_8);
- }
- }
- }
- if (email != null && password != null) {
- User user = loginService.login(email, password);
- if (user != null) {
- request.getSession().setAttribute(USER_ID_KEY, user.getId());
- LogAction.login(user.getId(), ServletHelper.retrieveRemoteAddress(request));
- return user;
- }
- }
-
- } else {
-
+ if (userId != null) {
User user = permissionsService.getUser(userId);
if (user != null) {
return user;
}
-
}
throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build());
@@ -123,32 +102,44 @@ public class SessionResource extends BaseResource {
@Path("{id}")
@GET
public User get(@PathParam("id") long userId) throws StorageException {
- permissionsService.checkAdmin(getUserId());
+ 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(), ServletHelper.retrieveRemoteAddress(request));
+ 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 = loginService.login(email, password);
- if (user != null) {
+ @FormParam("email") String email,
+ @FormParam("password") String password,
+ @FormParam("code") Integer code) throws StorageException {
+ LoginResult loginResult;
+ try {
+ loginResult = loginService.login(email, password, code);
+ } catch (CodeRequiredException e) {
+ Response response = Response
+ .status(Response.Status.UNAUTHORIZED)
+ .header("WWW-Authenticate", "TOTP")
+ .build();
+ throw new WebApplicationException(response);
+ }
+ if (loginResult != null) {
+ User user = loginResult.getUser();
request.getSession().setAttribute(USER_ID_KEY, user.getId());
- LogAction.login(user.getId(), ServletHelper.retrieveRemoteAddress(request));
+ 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(), ServletHelper.retrieveRemoteAddress(request));
+ LogAction.logout(getUserId(), WebHelper.retrieveRemoteAddress(request));
request.getSession().removeAttribute(USER_ID_KEY);
return Response.noContent().build();
}
@@ -157,7 +148,28 @@ public class SessionResource extends BaseResource {
@POST
public String requestToken(
@FormParam("expiration") Date expiration) throws StorageException, GeneralSecurityException, IOException {
+ Date currentExpiration = (Date) request.getSession().getAttribute(EXPIRATION_KEY);
+ if (currentExpiration != null && currentExpiration.before(expiration)) {
+ expiration = currentExpiration;
+ }
return tokenManager.generateToken(getUserId(), expiration);
}
+ @PermitAll
+ @Path("openid/auth")
+ @GET
+ public Response openIdAuth() {
+ 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 1f2296f28..0c728c77d 100644
--- a/src/main/java/org/traccar/api/resource/StatisticsResource.java
+++ b/src/main/java/org/traccar/api/resource/StatisticsResource.java
@@ -23,12 +23,12 @@ 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;
diff --git a/src/main/java/org/traccar/api/resource/UserResource.java b/src/main/java/org/traccar/api/resource/UserResource.java
index e41ebbe61..47ea9b07c 100644
--- a/src/main/java/org/traccar/api/resource/UserResource.java
+++ b/src/main/java/org/traccar/api/resource/UserResource.java
@@ -15,6 +15,11 @@
*/
package org.traccar.api.resource;
+import com.warrenstrange.googleauth.GoogleAuthenticator;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.ws.rs.DELETE;
+import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.core.Context;
import org.traccar.api.BaseObjectResource;
import org.traccar.config.Config;
import org.traccar.config.Keys;
@@ -28,18 +33,17 @@ 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.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 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;
@Path("users")
@Produces(MediaType.APPLICATION_JSON)
@@ -49,6 +53,9 @@ public class UserResource extends BaseObjectResource<User> {
@Inject
private Config config;
+ @Context
+ private HttpServletRequest request;
+
public UserResource() {
super(User.class);
}
@@ -91,11 +98,11 @@ public class UserResource extends BaseObjectResource<User> {
if (!permissionsService.getServer().getRegistration()) {
throw new SecurityException("Registration disabled");
}
- entity.setDeviceLimit(config.getInteger(Keys.USERS_DEFAULT_DEVICE_LIMIT));
- int expirationDays = config.getInteger(Keys.USERS_DEFAULT_EXPIRATION_DAYS);
- if (expirationDays > 0) {
- entity.setExpirationTime(new Date(System.currentTimeMillis() + expirationDays * 86400000L));
+ if (permissionsService.getServer().getBoolean(Keys.WEB_TOTP_FORCE.getKey())
+ && entity.getTotpKey() == null) {
+ throw new SecurityException("One-time password key is required");
}
+ UserUtil.setUserDefaults(entity, config);
}
}
@@ -117,4 +124,24 @@ public class UserResource extends BaseObjectResource<User> {
return Response.ok(entity).build();
}
+ @Path("{id}")
+ @DELETE
+ public Response remove(@PathParam("id") long id) throws Exception {
+ Response response = super.remove(id);
+ if (getUserId() == id) {
+ request.getSession().removeAttribute(SessionResource.USER_ID_KEY);
+ }
+ return response;
+ }
+
+ @Path("totp")
+ @PermitAll
+ @POST
+ public String generateTotpKey() throws StorageException {
+ if (!permissionsService.getServer().getBoolean(Keys.WEB_TOTP_ENABLE.getKey())) {
+ throw new SecurityException("One-time password is disabled");
+ }
+ return new GoogleAuthenticator().createCredentials().getKey();
+ }
+
}
diff --git a/src/main/java/org/traccar/protocol/NdtpV6FrameDecoder.java b/src/main/java/org/traccar/api/security/CodeRequiredException.java
index 5b610013a..d522c6540 100644
--- a/src/main/java/org/traccar/protocol/NdtpV6FrameDecoder.java
+++ b/src/main/java/org/traccar/api/security/CodeRequiredException.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -13,20 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.protocol;
+package org.traccar.api.security;
-import io.netty.buffer.ByteBuf;
-import io.netty.channel.Channel;
-import io.netty.channel.ChannelHandlerContext;
-import org.traccar.BaseFrameDecoder;
-
-public class NdtpV6FrameDecoder extends BaseFrameDecoder {
-
- @Override
- protected Object decode(
- ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
-
- return buf;
+public class CodeRequiredException extends SecurityException {
+ public CodeRequiredException() {
+ super("Code not provided");
}
-
}
diff --git a/src/main/java/org/traccar/api/security/LoginResult.java b/src/main/java/org/traccar/api/security/LoginResult.java
new file mode 100644
index 000000000..1fccc36d1
--- /dev/null
+++ b/src/main/java/org/traccar/api/security/LoginResult.java
@@ -0,0 +1,29 @@
+package org.traccar.api.security;
+
+import org.traccar.model.User;
+
+import java.util.Date;
+
+public class LoginResult {
+
+ private final User user;
+ private final Date expiration;
+
+ public LoginResult(User user) {
+ this(user, null);
+ }
+
+ public LoginResult(User user, Date expiration) {
+ this.user = user;
+ this.expiration = expiration;
+ }
+
+ public User getUser() {
+ return user;
+ }
+
+ public Date getExpiration() {
+ return expiration;
+ }
+
+}
diff --git a/src/main/java/org/traccar/api/security/LoginService.java b/src/main/java/org/traccar/api/security/LoginService.java
index 88bafcfb5..930c4fa46 100644
--- a/src/main/java/org/traccar/api/security/LoginService.java
+++ b/src/main/java/org/traccar/api/security/LoginService.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,10 +15,12 @@
*/
package org.traccar.api.security;
+import com.warrenstrange.googleauth.GoogleAuthenticator;
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;
@@ -26,46 +28,54 @@ import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Request;
-import javax.annotation.Nullable;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+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 {
+ public LoginResult login(String token) throws StorageException, GeneralSecurityException, IOException {
if (serviceAccountToken != null && serviceAccountToken.equals(token)) {
- return new ServiceAccountUser();
+ return new LoginResult(new ServiceAccountUser());
}
- long userId = tokenManager.verifyToken(token);
+ TokenManager.TokenData tokenData = tokenManager.verifyToken(token);
User user = storage.getObject(User.class, new Request(
- new Columns.All(), new Condition.Equals("id", userId)));
+ new Columns.All(), new Condition.Equals("id", tokenData.getUserId())));
if (user != null) {
checkUserEnabled(user);
}
- return user;
+ return new LoginResult(user, tokenData.getExpiration());
}
- public User login(String email, String password) throws StorageException {
+ public LoginResult login(String email, String password, Integer code) throws StorageException {
+ if (forceOpenId) {
+ return null;
+ }
+
email = email.trim();
User user = storage.getObject(User.class, new Request(
new Columns.All(),
@@ -75,20 +85,39 @@ public class LoginService {
if (user != null) {
if (ldapProvider != null && user.getLogin() != null && ldapProvider.login(user.getLogin(), password)
|| !forceLdap && user.isPasswordValid(password)) {
+ checkUserCode(user, code);
checkUserEnabled(user);
- return user;
+ return new LoginResult(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 new LoginResult(user);
}
}
return null;
}
+ public LoginResult 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) {
+ 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 new LoginResult(user);
+ }
+
private void checkUserEnabled(User user) throws SecurityException {
if (user == null) {
throw new SecurityException("Unknown account");
@@ -96,4 +125,17 @@ public class LoginService {
user.checkDisabled();
}
+ private void checkUserCode(User user, Integer code) throws SecurityException {
+ String key = user.getTotpKey();
+ if (key != null && !key.isEmpty()) {
+ if (code == null) {
+ throw new CodeRequiredException();
+ }
+ GoogleAuthenticator authenticator = new GoogleAuthenticator();
+ if (!authenticator.authorize(key, code)) {
+ throw new SecurityException("User authorization failed");
+ }
+ }
+ }
+
}
diff --git a/src/main/java/org/traccar/api/security/PermissionsService.java b/src/main/java/org/traccar/api/security/PermissionsService.java
index 4421572d7..d60bbafb8 100644
--- a/src/main/java/org/traccar/api/security/PermissionsService.java
+++ b/src/main/java/org/traccar/api/security/PermissionsService.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -23,7 +23,8 @@ import org.traccar.model.Device;
import org.traccar.model.Group;
import org.traccar.model.GroupedModel;
import org.traccar.model.ManagedUser;
-import org.traccar.model.ScheduledModel;
+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;
@@ -33,7 +34,7 @@ import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Request;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.util.Objects;
@RequestScoped
@@ -129,17 +130,17 @@ public class PermissionsService {
GroupedModel before = null;
if (!addition) {
before = storage.getObject(after.getClass(), new Request(
- new Columns.Include("groupId"), new Condition.Equals("id", object.getId())));
+ 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 ScheduledModel) {
- ScheduledModel after = ((ScheduledModel) object);
+ if (object instanceof Schedulable) {
+ Schedulable after = ((Schedulable) object);
if (after.getCalendarId() > 0) {
- ScheduledModel before = null;
+ Schedulable before = null;
if (!addition) {
before = storage.getObject(after.getClass(), new Request(
new Columns.Include("calendarId"), new Condition.Equals("id", object.getId())));
@@ -149,6 +150,19 @@ public class PermissionsService {
}
}
}
+ 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());
+ }
+ }
+ }
}
}
@@ -167,7 +181,7 @@ public class PermissionsService {
|| before.getUserLimit() != after.getUserLimit()) {
checkAdmin(userId);
}
- User user = getUser(userId);
+ User user = userId > 0 ? getUser(userId) : null;
if (user != null && user.getExpirationTime() != null
&& !Objects.equals(before.getExpirationTime(), after.getExpirationTime())
&& (after.getExpirationTime() == null
diff --git a/src/main/java/org/traccar/api/security/SecurityRequestFilter.java b/src/main/java/org/traccar/api/security/SecurityRequestFilter.java
index 94b6bbf05..12a5dbecf 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 - 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,6 +15,7 @@
*/
package org.traccar.api.security;
+import com.google.inject.Injector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.api.resource.SessionResource;
@@ -23,32 +24,26 @@ import org.traccar.helper.DataConverter;
import org.traccar.model.User;
import org.traccar.storage.StorageException;
-import javax.annotation.security.PermitAll;
-import javax.inject.Inject;
-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.Context;
-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;
+import java.util.Date;
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 BEARER_PREFIX = "Bearer ";
- 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);
@@ -70,6 +65,9 @@ public class SecurityRequestFilter implements ContainerRequestFilter {
@Inject
private StatisticsManager statisticsManager;
+ @Inject
+ private Injector injector;
+
@Override
public void filter(ContainerRequestContext requestContext) {
@@ -81,20 +79,22 @@ public class SecurityRequestFilter implements ContainerRequestFilter {
try {
- String authHeader = requestContext.getHeaderString(AUTHORIZATION_HEADER);
+ String authHeader = requestContext.getHeaderString("Authorization");
if (authHeader != null) {
try {
- User user;
- if (authHeader.startsWith(BEARER_PREFIX)) {
- user = loginService.login(authHeader.substring(BEARER_PREFIX.length()));
+ LoginResult loginResult;
+ if (authHeader.startsWith("Bearer ")) {
+ loginResult = loginService.login(authHeader.substring(7));
} else {
String[] auth = decodeBasicAuth(authHeader);
- user = loginService.login(auth[0], auth[1]);
+ loginResult = loginService.login(auth[0], auth[1], null);
}
- if (user != null) {
+ if (loginResult != null) {
+ User user = loginResult.getUser();
statisticsManager.registerRequest(user.getId());
- securityContext = new UserSecurityContext(new UserPrincipal(user.getId()));
+ securityContext = new UserSecurityContext(
+ new UserPrincipal(user.getId(), loginResult.getExpiration()));
}
} catch (StorageException | GeneralSecurityException | IOException e) {
throw new WebApplicationException(e);
@@ -103,14 +103,19 @@ public class SecurityRequestFilter implements ContainerRequestFilter {
} else if (request.getSession() != null) {
Long userId = (Long) request.getSession().getAttribute(SessionResource.USER_ID_KEY);
+ Date expiration = (Date) request.getSession().getAttribute(SessionResource.EXPIRATION_KEY);
if (userId != null) {
- statisticsManager.registerRequest(userId);
- securityContext = new UserSecurityContext(new UserPrincipal(userId));
+ User user = injector.getInstance(PermissionsService.class).getUser(userId);
+ if (user != null) {
+ user.checkDisabled();
+ statisticsManager.registerRequest(userId);
+ securityContext = new UserSecurityContext(new UserPrincipal(userId, expiration));
+ }
}
}
- } catch (SecurityException e) {
+ } catch (SecurityException | StorageException e) {
LOGGER.warn("Authentication error", e);
}
@@ -120,8 +125,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/api/security/UserPrincipal.java b/src/main/java/org/traccar/api/security/UserPrincipal.java
index 18b84a0e1..83bd06fe9 100644
--- a/src/main/java/org/traccar/api/security/UserPrincipal.java
+++ b/src/main/java/org/traccar/api/security/UserPrincipal.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,19 +16,26 @@
package org.traccar.api.security;
import java.security.Principal;
+import java.util.Date;
public class UserPrincipal implements Principal {
private final long userId;
+ private final Date expiration;
- public UserPrincipal(long userId) {
+ public UserPrincipal(long userId, Date expiration) {
this.userId = userId;
+ this.expiration = expiration;
}
public Long getUserId() {
return userId;
}
+ public Date getExpiration() {
+ return expiration;
+ }
+
@Override
public String getName() {
return null;
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
index 249d5bd97..71f56e0fb 100644
--- a/src/main/java/org/traccar/api/signature/CryptoManager.java
+++ b/src/main/java/org/traccar/api/signature/CryptoManager.java
@@ -20,8 +20,8 @@ import org.traccar.storage.StorageException;
import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Request;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyPair;
diff --git a/src/main/java/org/traccar/api/signature/TokenManager.java b/src/main/java/org/traccar/api/signature/TokenManager.java
index 6a0d90b40..824433b08 100644
--- a/src/main/java/org/traccar/api/signature/TokenManager.java
+++ b/src/main/java/org/traccar/api/signature/TokenManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -20,8 +20,8 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.codec.binary.Base64;
import org.traccar.storage.StorageException;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Date;
@@ -35,11 +35,19 @@ public class TokenManager {
private final ObjectMapper objectMapper;
private final CryptoManager cryptoManager;
- public static class Data {
+ public static class TokenData {
@JsonProperty("u")
private long userId;
@JsonProperty("e")
private Date expiration;
+
+ public long getUserId() {
+ return userId;
+ }
+
+ public Date getExpiration() {
+ return expiration;
+ }
}
@Inject
@@ -54,7 +62,7 @@ public class TokenManager {
public String generateToken(
long userId, Date expiration) throws IOException, GeneralSecurityException, StorageException {
- Data data = new Data();
+ TokenData data = new TokenData();
data.userId = userId;
if (expiration != null) {
data.expiration = expiration;
@@ -65,13 +73,13 @@ public class TokenManager {
return Base64.encodeBase64URLSafeString(cryptoManager.sign(encoded));
}
- public long verifyToken(String token) throws IOException, GeneralSecurityException, StorageException {
+ public TokenData verifyToken(String token) throws IOException, GeneralSecurityException, StorageException {
byte[] encoded = cryptoManager.verify(Base64.decodeBase64(token));
- Data data = objectMapper.readValue(encoded, Data.class);
+ TokenData data = objectMapper.readValue(encoded, TokenData.class);
if (data.expiration.before(new Date())) {
throw new SecurityException("Token has expired");
}
- return data.userId;
+ return data;
}
}
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..01b212c60
--- /dev/null
+++ b/src/main/java/org/traccar/broadcast/BaseBroadcastService.java
@@ -0,0 +1,128 @@
+/*
+ * 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.Set;
+
+import org.traccar.model.BaseModel;
+import org.traccar.model.Device;
+import org.traccar.model.Event;
+import org.traccar.model.ObjectOperation;
+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 <T extends BaseModel> void invalidateObject(
+ boolean local, Class<T> clazz, long id, ObjectOperation operation) {
+ BroadcastMessage message = new BroadcastMessage();
+ var invalidateObject = new BroadcastMessage.InvalidateObject();
+ invalidateObject.setClazz(Permission.getKey(clazz));
+ invalidateObject.setId(id);
+ invalidateObject.setOperation(operation);
+ message.setInvalidateObject(invalidateObject);
+ sendMessage(message);
+ }
+
+ @Override
+ public synchronized <T1 extends BaseModel, T2 extends BaseModel> void invalidatePermission(
+ boolean local, Class<T1> clazz1, long id1, Class<T2> clazz2, long id2, boolean link) {
+ BroadcastMessage message = new BroadcastMessage();
+ var invalidatePermission = new BroadcastMessage.InvalidatePermission();
+ invalidatePermission.setClazz1(Permission.getKey(clazz1));
+ invalidatePermission.setId1(id1);
+ invalidatePermission.setClazz2(Permission.getKey(clazz2));
+ invalidatePermission.setId2(id2);
+ invalidatePermission.setLink(link);
+ message.setInvalidatePermission(invalidatePermission);
+ sendMessage(message);
+ }
+
+ protected abstract void sendMessage(BroadcastMessage message);
+
+ protected void handleMessage(BroadcastMessage message) throws Exception {
+ 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.getInvalidateObject() != null) {
+ var invalidateObject = message.getInvalidateObject();
+ for (BroadcastInterface listener : listeners) {
+ listener.invalidateObject(
+ false,
+ Permission.getKeyClass(invalidateObject.getClazz()), invalidateObject.getId(),
+ invalidateObject.getOperation());
+ }
+ } else if (message.getInvalidatePermission() != null) {
+ var invalidatePermission = message.getInvalidatePermission();
+ for (BroadcastInterface listener : listeners) {
+ listener.invalidatePermission(
+ false,
+ Permission.getKeyClass(invalidatePermission.getClazz1()), invalidatePermission.getId1(),
+ Permission.getKeyClass(invalidatePermission.getClazz2()), invalidatePermission.getId2(),
+ invalidatePermission.getLink());
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/broadcast/BroadcastInterface.java b/src/main/java/org/traccar/broadcast/BroadcastInterface.java
index 673ebd8b8..d0a491cd2 100644
--- a/src/main/java/org/traccar/broadcast/BroadcastInterface.java
+++ b/src/main/java/org/traccar/broadcast/BroadcastInterface.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -18,6 +18,7 @@ package org.traccar.broadcast;
import org.traccar.model.BaseModel;
import org.traccar.model.Device;
import org.traccar.model.Event;
+import org.traccar.model.ObjectOperation;
import org.traccar.model.Position;
public interface BroadcastInterface {
@@ -34,12 +35,12 @@ public interface BroadcastInterface {
default void updateCommand(boolean local, long deviceId) {
}
- default void invalidateObject(boolean local, Class<? extends BaseModel> clazz, long id) {
+ default <T extends BaseModel> void invalidateObject(
+ boolean local, Class<T> clazz, long id, ObjectOperation operation) throws Exception {
}
- default void invalidatePermission(
- boolean local,
- Class<? extends BaseModel> clazz1, long id1,
- Class<? extends BaseModel> clazz2, long id2) {
+ default <T1 extends BaseModel, T2 extends BaseModel> void invalidatePermission(
+ boolean local, Class<T1> clazz1, long id1, Class<T2> clazz2, long id2, boolean link) throws Exception {
}
+
}
diff --git a/src/main/java/org/traccar/broadcast/BroadcastMessage.java b/src/main/java/org/traccar/broadcast/BroadcastMessage.java
index 985848d04..0d15d7495 100644
--- a/src/main/java/org/traccar/broadcast/BroadcastMessage.java
+++ b/src/main/java/org/traccar/broadcast/BroadcastMessage.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -17,10 +17,9 @@ package org.traccar.broadcast;
import org.traccar.model.Device;
import org.traccar.model.Event;
+import org.traccar.model.ObjectOperation;
import org.traccar.model.Position;
-import java.util.Map;
-
public class BroadcastMessage {
private Device device;
@@ -73,13 +72,112 @@ public class BroadcastMessage {
this.commandDeviceId = commandDeviceId;
}
- private Map<String, Long> changes;
+ public static class InvalidateObject {
+
+ private String clazz;
+
+ public String getClazz() {
+ return clazz;
+ }
+
+ public void setClazz(String clazz) {
+ this.clazz = clazz;
+ }
+
+ private long id;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ private ObjectOperation operation;
+
+ public ObjectOperation getOperation() {
+ return operation;
+ }
+
+ public void setOperation(ObjectOperation operation) {
+ this.operation = operation;
+ }
- public Map<String, Long> getChanges() {
- return changes;
}
- public void setChanges(Map<String, Long> changes) {
- this.changes = changes;
+ private InvalidateObject invalidateObject;
+
+ public InvalidateObject getInvalidateObject() {
+ return invalidateObject;
+ }
+
+ public void setInvalidateObject(InvalidateObject invalidateObject) {
+ this.invalidateObject = invalidateObject;
}
+
+ public static class InvalidatePermission {
+
+ private String clazz1;
+
+ public String getClazz1() {
+ return clazz1;
+ }
+
+ public void setClazz1(String clazz1) {
+ this.clazz1 = clazz1;
+ }
+
+ private long id1;
+
+ public long getId1() {
+ return id1;
+ }
+
+ public void setId1(long id1) {
+ this.id1 = id1;
+ }
+
+ private String clazz2;
+
+ public String getClazz2() {
+ return clazz2;
+ }
+
+ public void setClazz2(String clazz2) {
+ this.clazz2 = clazz2;
+ }
+
+ private long id2;
+
+ public long getId2() {
+ return id2;
+ }
+
+ public void setId2(long id2) {
+ this.id2 = id2;
+ }
+
+ private boolean link;
+
+ public boolean getLink() {
+ return link;
+ }
+
+ public void setLink(boolean link) {
+ this.link = link;
+ }
+
+ }
+
+ private InvalidatePermission invalidatePermission;
+
+ public InvalidatePermission getInvalidatePermission() {
+ return invalidatePermission;
+ }
+
+ public void setInvalidatePermission(InvalidatePermission invalidatePermission) {
+ this.invalidatePermission = invalidatePermission;
+ }
+
}
diff --git a/src/main/java/org/traccar/broadcast/MulticastBroadcastService.java b/src/main/java/org/traccar/broadcast/MulticastBroadcastService.java
index b1b66f1e3..793c6df36 100644
--- a/src/main/java/org/traccar/broadcast/MulticastBroadcastService.java
+++ b/src/main/java/org/traccar/broadcast/MulticastBroadcastService.java
@@ -20,11 +20,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 java.io.IOException;
import java.net.DatagramPacket;
@@ -34,13 +29,10 @@ import java.net.InetSocketAddress;
import java.net.MulticastSocket;
import java.net.NetworkInterface;
import java.nio.charset.StandardCharsets;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
-public class MulticastBroadcastService implements BroadcastService {
+public class MulticastBroadcastService extends BaseBroadcastService {
private static final Logger LOGGER = LoggerFactory.getLogger(MulticastBroadcastService.class);
@@ -55,8 +47,6 @@ public class MulticastBroadcastService implements BroadcastService {
private final ExecutorService service = Executors.newSingleThreadExecutor();
private final byte[] receiverBuffer = new byte[4096];
- private final Set<BroadcastInterface> listeners = new HashSet<>();
-
public MulticastBroadcastService(Config config, ObjectMapper objectMapper) throws IOException {
this.objectMapper = objectMapper;
port = config.getInteger(Keys.BROADCAST_PORT);
@@ -76,57 +66,7 @@ public class MulticastBroadcastService implements BroadcastService {
}
@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);
- }
-
- private void sendMessage(BroadcastMessage message) {
+ protected void sendMessage(BroadcastMessage message) {
try {
byte[] buffer = objectMapper.writeValueAsString(message).getBytes(StandardCharsets.UTF_8);
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, group);
@@ -136,34 +76,6 @@ public class MulticastBroadcastService implements BroadcastService {
}
}
- private 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()));
- }
- }
- }
- }
-
@Override
public void start() throws IOException {
service.submit(receiver);
@@ -191,7 +103,7 @@ public class MulticastBroadcastService implements BroadcastService {
}
publisherSocket = null;
socket.leaveGroup(group, networkInterface);
- } catch (IOException e) {
+ } catch (Exception e) {
throw new RuntimeException(e);
}
}
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..697c45a4a
--- /dev/null
+++ b/src/main/java/org/traccar/broadcast/RedisBroadcastService.java
@@ -0,0 +1,125 @@
+/*
+ * 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 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;
+ String 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 | 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 (Exception e) {
+ LOGGER.warn("Broadcast handleMessage failed", e);
+ }
+ }
+ }, channel);
+ } 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 c73be6475..47e1f0707 100644
--- a/src/main/java/org/traccar/config/Config.java
+++ b/src/main/java/org/traccar/config/Config.java
@@ -19,8 +19,8 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.inject.name.Named;
import org.traccar.helper.Log;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
diff --git a/src/main/java/org/traccar/config/Keys.java b/src/main/java/org/traccar/config/Keys.java
index 093f4298c..02e684875 100644
--- a/src/main/java/org/traccar/config/Keys.java
+++ b/src/main/java/org/traccar/config/Keys.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2024 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -234,11 +234,18 @@ public final class Keys {
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.DEVICE));
+ List.of(KeyType.CONFIG, KeyType.DEVICE));
/**
* Protocol date format. Used by protocols that have configurable date format.
@@ -293,6 +300,13 @@ public final class Keys {
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).
*/
@@ -327,6 +341,22 @@ public final class Keys {
0.0);
/**
+ * Disable device sharing on the server.
+ */
+ public static final ConfigKey<Boolean> DEVICE_SHARE_DISABLE = new BooleanConfigKey(
+ "disableShare",
+ List.of(KeyType.SERVER));
+
+ /**
+ * 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<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 LongConfigKey(
@@ -366,14 +396,15 @@ public final class Keys {
*/
public static final ConfigKey<Boolean> EVENT_MOTION_PROCESS_INVALID_POSITIONS = new BooleanConfigKey(
"event.motion.processInvalidPositions",
- List.of(KeyType.CONFIG));
+ 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 DoubleConfigKey(
"event.motion.speedThreshold",
- List.of(KeyType.CONFIG),
+ List.of(KeyType.CONFIG, KeyType.DEVICE),
0.01);
/**
@@ -386,6 +417,13 @@ public final class Keys {
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.
*/
@@ -459,14 +497,6 @@ public final class Keys {
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 BooleanConfigKey(
- "database.ignoreUnknown",
- List.of(KeyType.CONFIG));
-
- /**
* Automatically register unknown devices in the database.
*/
public static final ConfigKey<Boolean> DATABASE_REGISTER_UNKNOWN = new BooleanConfigKey(
@@ -488,6 +518,13 @@ public final class Keys {
List.of(KeyType.CONFIG));
/**
+ * Automatically register unknown devices with regex filter.
+ */
+ 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 BooleanConfigKey(
@@ -590,6 +627,85 @@ public final class Keys {
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, e.g. 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.
*/
@@ -638,6 +754,14 @@ public final class Keys {
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.
*/
@@ -653,12 +777,19 @@ public final class Keys {
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<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",
List.of(KeyType.CONFIG),
- 60000L);
+ 300000L);
/**
* Authentication sessions timeout in seconds. By default no timeout.
@@ -706,6 +837,27 @@ public final class Keys {
"max-age=3600,public");
/**
+ * Enable TOTP authentication on the server.
+ */
+ public static final ConfigKey<Boolean> WEB_TOTP_ENABLE = new BooleanConfigKey(
+ "totpEnable",
+ List.of(KeyType.SERVER));
+
+ /**
+ * Server attribute that indicates that TOTP authentication is required for new users.
+ */
+ public static final ConfigKey<Boolean> WEB_TOTP_FORCE = new BooleanConfigKey(
+ "totpForce",
+ List.of(KeyType.SERVER));
+
+ /**
+ * Host for raw data forwarding.
+ */
+ public static final ConfigKey<String> SERVER_FORWARD = new StringConfigKey(
+ "server.forward",
+ List.of(KeyType.CONFIG));
+
+ /**
* Position forwarding format. Available options are "url", "json" and "kafka". Default is "url".
*/
public static final ConfigKey<String> FORWARD_TYPE = new StringConfigKey(
@@ -714,7 +866,15 @@ public final class Keys {
"url");
/**
- * Position forwarding Kafka topic.
+ * Position forwarding AMQP exchange.
+ */
+ public static final ConfigKey<String> FORWARD_EXCHANGE = new StringConfigKey(
+ "forward.exchange",
+ List.of(KeyType.CONFIG),
+ "traccar");
+
+ /**
+ * Position forwarding Kafka topic or AQMP Routing Key.
*/
public static final ConfigKey<String> FORWARD_TOPIC = new StringConfigKey(
"forward.topic",
@@ -783,7 +943,15 @@ public final class Keys {
"json");
/**
- * Events forwarding Kafka topic.
+ * 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",
@@ -822,6 +990,13 @@ public final class Keys {
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(
@@ -992,6 +1167,15 @@ public final class Keys {
List.of(KeyType.CONFIG));
/**
+ * If the event time is too old, we should not send notifications. This parameter is the threshold value in
+ * milliseconds. Default value is 15 minutes.
+ */
+ public static final ConfigKey<Long> NOTIFICATOR_TIME_THRESHOLD = new LongConfigKey(
+ "notificator.timeThreshold",
+ List.of(KeyType.CONFIG),
+ 15 * 60 * 1000L);
+
+ /**
* Traccar notification API key.
*/
public static final ConfigKey<String> NOTIFICATOR_TRACCAR_KEY = new StringConfigKey(
@@ -1041,19 +1225,56 @@ public final class Keys {
List.of(KeyType.CONFIG));
/**
+ * Enable user expiration email notification.
+ */
+ public static final ConfigKey<Boolean> NOTIFICATION_EXPIRATION_USER = new BooleanConfigKey(
+ "notification.expiration.user",
+ List.of(KeyType.CONFIG));
+
+ /**
+ * User expiration reminder. Value in milliseconds.
+ */
+ public static final ConfigKey<Long> NOTIFICATION_EXPIRATION_USER_REMINDER = new LongConfigKey(
+ "notification.expiration.user.reminder",
+ List.of(KeyType.CONFIG));
+
+ /**
+ * Enable device expiration email notification.
+ */
+ public static final ConfigKey<Boolean> NOTIFICATION_EXPIRATION_DEVICE = new BooleanConfigKey(
+ "notification.expiration.device",
+ List.of(KeyType.CONFIG));
+
+ /**
+ * Device expiration reminder. Value in milliseconds.
+ */
+ public static final ConfigKey<Long> NOTIFICATION_EXPIRATION_DEVICE_REMINDER = new LongConfigKey(
+ "notification.expiration.device.reminder",
+ 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.
+ * By default, there is no limit.
*/
public static final ConfigKey<Long> REPORT_PERIOD_LIMIT = new LongConfigKey(
"report.periodLimit",
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 LongConfigKey(
"report.trip.minimalTripDistance",
- List.of(KeyType.CONFIG),
+ List.of(KeyType.CONFIG, KeyType.DEVICE),
500L);
/**
@@ -1061,7 +1282,7 @@ public final class Keys {
*/
public static final ConfigKey<Long> REPORT_TRIP_MINIMAL_TRIP_DURATION = new LongConfigKey(
"report.trip.minimalTripDuration",
- List.of(KeyType.CONFIG),
+ List.of(KeyType.CONFIG, KeyType.DEVICE),
300L);
/**
@@ -1069,7 +1290,7 @@ public final class Keys {
*/
public static final ConfigKey<Long> REPORT_TRIP_MINIMAL_PARKING_DURATION = new LongConfigKey(
"report.trip.minimalParkingDuration",
- List.of(KeyType.CONFIG),
+ List.of(KeyType.CONFIG, KeyType.DEVICE),
300L);
/**
@@ -1077,7 +1298,7 @@ public final class Keys {
*/
public static final ConfigKey<Long> REPORT_TRIP_MINIMAL_NO_DATA_DURATION = new LongConfigKey(
"report.trip.minimalNoDataDuration",
- List.of(KeyType.CONFIG),
+ List.of(KeyType.CONFIG, KeyType.DEVICE),
3600L);
/**
@@ -1085,7 +1306,8 @@ public final class Keys {
*/
public static final ConfigKey<Boolean> REPORT_TRIP_USE_IGNITION = new BooleanConfigKey(
"report.trip.useIgnition",
- List.of(KeyType.CONFIG));
+ List.of(KeyType.CONFIG, KeyType.DEVICE),
+ false);
/**
* Ignore odometer value reported by the device and use server-calculated total distance instead. This is useful
@@ -1093,7 +1315,8 @@ public final class Keys {
*/
public static final ConfigKey<Boolean> REPORT_IGNORE_ODOMETER = new BooleanConfigKey(
"report.ignoreOdometer",
- List.of(KeyType.CONFIG));
+ List.of(KeyType.CONFIG),
+ false);
/**
* Boolean flag to enable or disable position filtering.
@@ -1125,6 +1348,14 @@ public final class Keys {
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 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.
*/
@@ -1187,6 +1418,13 @@ public final class Keys {
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).
@@ -1294,13 +1532,42 @@ public final class Keys {
List.of(KeyType.CONFIG, KeyType.DEVICE));
/**
- * Enable computed attributes processing.
+ * Include device attributes in the computed attribute context.
*/
public static final ConfigKey<Boolean> PROCESSING_COMPUTED_ATTRIBUTES_DEVICE_ATTRIBUTES = new BooleanConfigKey(
"processing.computedAttributes.deviceAttributes",
List.of(KeyType.CONFIG));
/**
+ * Include last position attributes in the computed attribute context.
+ */
+ public static final ConfigKey<Boolean> PROCESSING_COMPUTED_ATTRIBUTES_LAST_ATTRIBUTES = new BooleanConfigKey(
+ "processing.computedAttributes.lastAttributes",
+ 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 BooleanConfigKey(
@@ -1323,13 +1590,6 @@ public final class Keys {
List.of(KeyType.CONFIG));
/**
- * App id for use with Here provider.
- */
- public static final ConfigKey<String> GEOCODER_ID = new StringConfigKey(
- "geocoder.id",
- List.of(KeyType.CONFIG));
-
- /**
* Provider API key. Most providers require API keys.
*/
public static final ConfigKey<String> GEOCODER_KEY = new StringConfigKey(
@@ -1433,6 +1693,13 @@ public final class Keys {
List.of(KeyType.CONFIG));
/**
+ * Process geolocation only when Wi-Fi information is available. This makes the result more accurate.
+ */
+ public static final ConfigKey<Boolean> GEOLOCATION_REQUIRE_WIFI = new BooleanConfigKey(
+ "geolocation.requireWifi",
+ List.of(KeyType.CONFIG));
+
+ /**
* Default MCC value to use if device doesn't report MCC.
*/
public static final ConfigKey<Integer> GEOLOCATION_MCC = new IntegerConfigKey(
@@ -1468,6 +1735,14 @@ public final class Keys {
List.of(KeyType.CONFIG));
/**
+ * Search radius for speed limit. Value is in meters. Default value is 100.
+ */
+ public static final ConfigKey<Integer> SPEED_LIMIT_ACCURACY = new IntegerConfigKey(
+ "speedLimit.accuracy",
+ List.of(KeyType.CONFIG),
+ 100);
+
+ /**
* 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.
*/
@@ -1523,7 +1798,7 @@ public final class Keys {
List.of(KeyType.CONFIG));
/**
- * Public URL for the web app. Used for notification and report link.
+ * 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(
@@ -1531,6 +1806,27 @@ public final class Keys {
List.of(KeyType.CONFIG));
/**
+ * Show logs from unknown devices.
+ */
+ public static final ConfigKey<Boolean> WEB_SHOW_UNKNOWN_DEVICES = new BooleanConfigKey(
+ "web.showUnknownDevices",
+ List.of(KeyType.CONFIG));
+
+ /**
+ * Enable commands for a shared device.
+ */
+ public static final ConfigKey<Boolean> WEB_SHARE_DEVICE_COMMANDS = new BooleanConfigKey(
+ "web.shareDevice.commands",
+ List.of(KeyType.CONFIG));
+
+ /**
+ * Enable reports for a shared device.
+ */
+ public static final ConfigKey<Boolean> WEB_SHARE_DEVICE_REPORTS = new BooleanConfigKey(
+ "web.shareDevice.reports",
+ List.of(KeyType.CONFIG));
+
+ /**
* Output logging to the standard terminal output instead of a log file.
*/
public static final ConfigKey<Boolean> LOGGER_CONSOLE = new BooleanConfigKey(
@@ -1593,6 +1889,14 @@ public final class Keys {
"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(
@@ -1600,7 +1904,7 @@ public final class Keys {
List.of(KeyType.CONFIG));
/**
- * Multicast address for broadcasting synchronization events.
+ * Multicast address or Redis URL for broadcasting synchronization events.
*/
public static final ConfigKey<String> BROADCAST_ADDRESS = new StringConfigKey(
"broadcast.address",
diff --git a/src/main/java/org/traccar/database/CommandsManager.java b/src/main/java/org/traccar/database/CommandsManager.java
index df399cd7a..90180b989 100644
--- a/src/main/java/org/traccar/database/CommandsManager.java
+++ b/src/main/java/org/traccar/database/CommandsManager.java
@@ -22,6 +22,7 @@ import org.traccar.broadcast.BroadcastInterface;
import org.traccar.broadcast.BroadcastService;
import org.traccar.model.Command;
import org.traccar.model.Device;
+import org.traccar.model.Event;
import org.traccar.model.Position;
import org.traccar.model.QueuedCommand;
import org.traccar.session.ConnectionManager;
@@ -34,10 +35,12 @@ import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Order;
import org.traccar.storage.query.Request;
-import javax.annotation.Nullable;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.annotation.Nullable;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
import java.util.stream.Collectors;
@Singleton
@@ -48,20 +51,23 @@ public class CommandsManager implements BroadcastInterface {
private final SmsManager smsManager;
private final ConnectionManager connectionManager;
private final BroadcastService broadcastService;
+ private final NotificationManager notificationManager;
@Inject
public CommandsManager(
Storage storage, ServerManager serverManager, @Nullable SmsManager smsManager,
- ConnectionManager connectionManager, BroadcastService broadcastService) {
+ ConnectionManager connectionManager, BroadcastService broadcastService,
+ NotificationManager notificationManager) {
this.storage = storage;
this.serverManager = serverManager;
this.smsManager = smsManager;
this.connectionManager = connectionManager;
this.broadcastService = broadcastService;
+ this.notificationManager = notificationManager;
broadcastService.registerListener(this);
}
- public boolean sendCommand(Command command) throws Exception {
+ public QueuedCommand sendCommand(Command command) throws Exception {
long deviceId = command.getDeviceId();
if (command.getTextChannel()) {
if (smsManager == null) {
@@ -84,12 +90,13 @@ public class CommandsManager implements BroadcastInterface {
if (deviceSession != null && deviceSession.supportsLiveCommands()) {
deviceSession.sendCommand(command);
} else {
- storage.addObject(QueuedCommand.fromCommand(command), new Request(new Columns.Exclude("id")));
+ QueuedCommand queuedCommand = QueuedCommand.fromCommand(command);
+ queuedCommand.setId(storage.addObject(queuedCommand, new Request(new Columns.Exclude("id"))));
broadcastService.updateCommand(true, deviceId);
- return false;
+ return queuedCommand;
}
}
- return true;
+ return null;
}
public Collection<Command> readQueuedCommands(long deviceId) {
@@ -102,10 +109,16 @@ public class CommandsManager implements BroadcastInterface {
new Columns.All(),
new Condition.Equals("deviceId", deviceId),
new Order("id", false, count)));
+ Map<Event, Position> events = new HashMap<>();
for (var command : commands) {
storage.removeObject(QueuedCommand.class, new Request(
new Condition.Equals("id", command.getId())));
+
+ Event event = new Event(Event.TYPE_QUEUED_COMMAND_SENT, command.getDeviceId());
+ event.set("id", command.getId());
+ events.put(event, null);
}
+ notificationManager.updateEvents(events);
return commands.stream().map(QueuedCommand::toCommand).collect(Collectors.toList());
} catch (StorageException e) {
throw new RuntimeException(e);
diff --git a/src/main/java/org/traccar/database/DeviceLookupService.java b/src/main/java/org/traccar/database/DeviceLookupService.java
index 583b2ae35..90d23531e 100644
--- a/src/main/java/org/traccar/database/DeviceLookupService.java
+++ b/src/main/java/org/traccar/database/DeviceLookupService.java
@@ -29,8 +29,8 @@ import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Request;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@@ -49,7 +49,7 @@ public class DeviceLookupService {
private final boolean throttlingEnabled;
- private static class IdentifierInfo {
+ private static final class IdentifierInfo {
private long lastQuery;
private long delay;
private Timeout timeout;
diff --git a/src/main/java/org/traccar/database/MediaManager.java b/src/main/java/org/traccar/database/MediaManager.java
index c1ef810ee..2f2369c96 100644
--- a/src/main/java/org/traccar/database/MediaManager.java
+++ b/src/main/java/org/traccar/database/MediaManager.java
@@ -21,8 +21,8 @@ import org.slf4j.LoggerFactory;
import org.traccar.config.Config;
import org.traccar.config.Keys;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
diff --git a/src/main/java/org/traccar/database/NotificationManager.java b/src/main/java/org/traccar/database/NotificationManager.java
index cb971b082..65437f0a1 100644
--- a/src/main/java/org/traccar/database/NotificationManager.java
+++ b/src/main/java/org/traccar/database/NotificationManager.java
@@ -23,12 +23,12 @@ import org.traccar.config.Keys;
import org.traccar.forward.EventData;
import org.traccar.forward.EventForwarder;
import org.traccar.geocoder.Geocoder;
+import org.traccar.helper.DateUtil;
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.notification.MessageException;
import org.traccar.notification.NotificatorManager;
@@ -38,9 +38,9 @@ import org.traccar.storage.StorageException;
import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Request;
-import javax.annotation.Nullable;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+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;
@@ -58,6 +58,7 @@ public class NotificationManager {
private final Geocoder geocoder;
private final boolean geocodeOnRequest;
+ private final long timeThreshold;
@Inject
public NotificationManager(
@@ -69,6 +70,7 @@ public class NotificationManager {
this.notificatorManager = notificatorManager;
this.geocoder = geocoder;
geocodeOnRequest = config.getBoolean(Keys.GEOCODER_ON_REQUEST);
+ timeThreshold = config.getLong(Keys.NOTIFICATOR_TIME_THRESHOLD);
}
private void updateEvent(Event event, Position position) {
@@ -78,7 +80,14 @@ public class NotificationManager {
LOGGER.warn("Event save error", error);
}
- var notifications = cacheManager.getDeviceObjects(event.getDeviceId(), Notification.class).stream()
+ forwardEvent(event, position);
+
+ if (System.currentTimeMillis() - event.getEventTime().getTime() > timeThreshold) {
+ LOGGER.info("Skipping notifications for old event");
+ return;
+ }
+
+ var notifications = cacheManager.getDeviceNotifications(event.getDeviceId()).stream()
.filter(notification -> notification.getType().equals(event.getType()))
.filter(notification -> {
if (event.getType().equals(Event.TYPE_ALARM)) {
@@ -98,6 +107,14 @@ public class NotificationManager {
})
.collect(Collectors.toUnmodifiableList());
+ Device device = cacheManager.getObject(Device.class, event.getDeviceId());
+ LOGGER.info(
+ "Event id: {}, time: {}, type: {}, notifications: {}",
+ device.getUniqueId(),
+ DateUtil.formatDate(event.getEventTime(), false),
+ event.getType(),
+ notifications.size());
+
if (!notifications.isEmpty()) {
if (position != null && position.getAddress() == null && geocodeOnRequest && geocoder != null) {
position.setAddress(geocoder.getAddress(position.getLatitude(), position.getLongitude(), null));
@@ -107,16 +124,14 @@ public class NotificationManager {
cacheManager.getNotificationUsers(notification.getId(), event.getDeviceId()).forEach(user -> {
for (String notificator : notification.getNotificatorsTypes()) {
try {
- notificatorManager.getNotificator(notificator).send(user, event, position);
- } catch (MessageException | InterruptedException exception) {
+ notificatorManager.getNotificator(notificator).send(notification, user, event, position);
+ } catch (MessageException exception) {
LOGGER.warn("Notification failed", exception);
}
}
});
});
}
-
- forwardEvent(event, position);
}
private void forwardEvent(Event event, Position position) {
@@ -146,7 +161,7 @@ public class NotificationManager {
try {
cacheManager.addDevice(event.getDeviceId());
updateEvent(event, position);
- } catch (StorageException e) {
+ } catch (Exception e) {
throw new RuntimeException(e);
} finally {
cacheManager.removeDevice(event.getDeviceId());
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..93297f7ab
--- /dev/null
+++ b/src/main/java/org/traccar/database/OpenIdProvider.java
@@ -0,0 +1,204 @@
+/*
+ * 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 final URI callbackUrl;
+ private final URI authUrl;
+ private final URI tokenUrl;
+ private final URI userInfoUrl;
+ private final URI baseUrl;
+ private final String adminGroup;
+ private final String allowGroup;
+
+ private final 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<>() {
+ });
+
+ 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).getUser();
+
+ 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/StatisticsManager.java b/src/main/java/org/traccar/database/StatisticsManager.java
index e0995dabc..445e53e7c 100644
--- a/src/main/java/org/traccar/database/StatisticsManager.java
+++ b/src/main/java/org/traccar/database/StatisticsManager.java
@@ -19,6 +19,7 @@ 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;
@@ -28,11 +29,11 @@ import org.traccar.storage.StorageException;
import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Request;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-import javax.ws.rs.client.Client;
-import javax.ws.rs.client.Entity;
-import javax.ws.rs.core.Form;
+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;
@@ -57,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;
@@ -98,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;
@@ -138,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));
}
@@ -147,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);
}
}
@@ -162,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/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/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
index 7527d568a..df53d3d46 100644
--- a/src/main/java/org/traccar/forward/EventForwarderJson.java
+++ b/src/main/java/org/traccar/forward/EventForwarderJson.java
@@ -18,10 +18,10 @@ package org.traccar.forward;
import org.traccar.config.Config;
import org.traccar.config.Keys;
-import javax.ws.rs.client.Client;
-import javax.ws.rs.client.Entity;
-import javax.ws.rs.client.InvocationCallback;
-import javax.ws.rs.core.Response;
+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 {
diff --git a/src/main/java/org/traccar/forward/EventForwarderMqtt.java b/src/main/java/org/traccar/forward/EventForwarderMqtt.java
index dc95cb4e2..7f4e29384 100644
--- a/src/main/java/org/traccar/forward/EventForwarderMqtt.java
+++ b/src/main/java/org/traccar/forward/EventForwarderMqtt.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -21,8 +21,6 @@ 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.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.traccar.config.Config;
import org.traccar.config.Keys;
@@ -32,7 +30,6 @@ import java.util.UUID;
public class EventForwarderMqtt implements EventForwarder {
- private static final Logger LOGGER = LoggerFactory.getLogger(EventForwarderMqtt.class);
private final Mqtt5AsyncClient client;
private final ObjectMapper objectMapper;
@@ -63,7 +60,7 @@ public class EventForwarderMqtt implements EventForwarder {
String host = url.getHost();
int port = url.getPort();
client = Mqtt5Client.builder()
- .identifier("traccar-" + UUID.randomUUID().toString())
+ .identifier("traccar-" + UUID.randomUUID())
.serverHost(host)
.serverPort(port)
.simpleAuth(simpleAuth)
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/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
index 27b96308e..a0ad8ffd0 100644
--- a/src/main/java/org/traccar/forward/PositionForwarderJson.java
+++ b/src/main/java/org/traccar/forward/PositionForwarderJson.java
@@ -20,12 +20,12 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import org.traccar.config.Config;
import org.traccar.config.Keys;
-import javax.ws.rs.client.Client;
-import javax.ws.rs.client.Entity;
-import javax.ws.rs.client.InvocationCallback;
-import javax.ws.rs.core.HttpHeaders;
-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.InvocationCallback;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
public class PositionForwarderJson implements PositionForwarder {
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
index 53cc7ad24..33474d40b 100644
--- a/src/main/java/org/traccar/forward/PositionForwarderUrl.java
+++ b/src/main/java/org/traccar/forward/PositionForwarderUrl.java
@@ -23,9 +23,9 @@ import org.traccar.helper.Checksum;
import org.traccar.model.Device;
import org.traccar.model.Position;
-import javax.ws.rs.client.Client;
-import javax.ws.rs.client.InvocationCallback;
-import javax.ws.rs.core.Response;
+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;
diff --git a/src/main/java/org/traccar/geocoder/BanGeocoder.java b/src/main/java/org/traccar/geocoder/BanGeocoder.java
index f878a8bab..e2ff72311 100644
--- a/src/main/java/org/traccar/geocoder/BanGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/BanGeocoder.java
@@ -20,9 +20,9 @@ package org.traccar.geocoder;
* API documentation: https://adresse.data.gouv.fr/api
*/
-import javax.json.JsonArray;
-import javax.json.JsonObject;
-import javax.ws.rs.client.Client;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.ws.rs.client.Client;
public class BanGeocoder extends JsonGeocoder {
diff --git a/src/main/java/org/traccar/geocoder/BingMapsGeocoder.java b/src/main/java/org/traccar/geocoder/BingMapsGeocoder.java
index 01e33c2ea..bc3b15ce7 100644
--- a/src/main/java/org/traccar/geocoder/BingMapsGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/BingMapsGeocoder.java
@@ -16,9 +16,9 @@
*/
package org.traccar.geocoder;
-import javax.json.JsonArray;
-import javax.json.JsonObject;
-import javax.ws.rs.client.Client;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.ws.rs.client.Client;
public class BingMapsGeocoder extends JsonGeocoder {
diff --git a/src/main/java/org/traccar/geocoder/FactualGeocoder.java b/src/main/java/org/traccar/geocoder/FactualGeocoder.java
index 384f46b0e..6c8891316 100644
--- a/src/main/java/org/traccar/geocoder/FactualGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/FactualGeocoder.java
@@ -16,8 +16,8 @@
*/
package org.traccar.geocoder;
-import javax.json.JsonObject;
-import javax.ws.rs.client.Client;
+import jakarta.json.JsonObject;
+import jakarta.ws.rs.client.Client;
public class FactualGeocoder extends JsonGeocoder {
diff --git a/src/main/java/org/traccar/geocoder/GeoapifyGeocoder.java b/src/main/java/org/traccar/geocoder/GeoapifyGeocoder.java
index 4748d6a2c..35a47bb88 100644
--- a/src/main/java/org/traccar/geocoder/GeoapifyGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/GeoapifyGeocoder.java
@@ -15,9 +15,9 @@
*/
package org.traccar.geocoder;
-import javax.json.JsonArray;
-import javax.json.JsonObject;
-import javax.ws.rs.client.Client;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.ws.rs.client.Client;
public class GeoapifyGeocoder extends JsonGeocoder {
diff --git a/src/main/java/org/traccar/geocoder/GeocodeFarmGeocoder.java b/src/main/java/org/traccar/geocoder/GeocodeFarmGeocoder.java
index 2af95910f..80b00b3cc 100644
--- a/src/main/java/org/traccar/geocoder/GeocodeFarmGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/GeocodeFarmGeocoder.java
@@ -15,8 +15,8 @@
*/
package org.traccar.geocoder;
-import javax.json.JsonObject;
-import javax.ws.rs.client.Client;
+import jakarta.json.JsonObject;
+import jakarta.ws.rs.client.Client;
public class GeocodeFarmGeocoder extends JsonGeocoder {
diff --git a/src/main/java/org/traccar/geocoder/GeocodeXyzGeocoder.java b/src/main/java/org/traccar/geocoder/GeocodeXyzGeocoder.java
index 96491ece3..e88962e1a 100644
--- a/src/main/java/org/traccar/geocoder/GeocodeXyzGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/GeocodeXyzGeocoder.java
@@ -15,8 +15,8 @@
*/
package org.traccar.geocoder;
-import javax.json.JsonObject;
-import javax.ws.rs.client.Client;
+import jakarta.json.JsonObject;
+import jakarta.ws.rs.client.Client;
public class GeocodeXyzGeocoder extends JsonGeocoder {
diff --git a/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java b/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java
index 0589eb000..062e795eb 100644
--- a/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java
@@ -15,8 +15,8 @@
*/
package org.traccar.geocoder;
-import javax.json.JsonObject;
-import javax.ws.rs.client.Client;
+import jakarta.json.JsonObject;
+import jakarta.ws.rs.client.Client;
public class GisgraphyGeocoder extends JsonGeocoder {
diff --git a/src/main/java/org/traccar/geocoder/GoogleGeocoder.java b/src/main/java/org/traccar/geocoder/GoogleGeocoder.java
index 4d9ec8f36..93f128b46 100644
--- a/src/main/java/org/traccar/geocoder/GoogleGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/GoogleGeocoder.java
@@ -15,10 +15,10 @@
*/
package org.traccar.geocoder;
-import javax.json.JsonArray;
-import javax.json.JsonObject;
-import javax.json.JsonString;
-import javax.ws.rs.client.Client;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonString;
+import jakarta.ws.rs.client.Client;
public class GoogleGeocoder extends JsonGeocoder {
diff --git a/src/main/java/org/traccar/geocoder/HereGeocoder.java b/src/main/java/org/traccar/geocoder/HereGeocoder.java
index eb639995e..4767eabad 100644
--- a/src/main/java/org/traccar/geocoder/HereGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/HereGeocoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 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.
@@ -15,70 +15,64 @@
*/
package org.traccar.geocoder;
-import javax.json.JsonObject;
-import javax.ws.rs.client.Client;
+import jakarta.json.JsonObject;
+import jakarta.ws.rs.client.Client;
public class HereGeocoder extends JsonGeocoder {
- private static String formatUrl(String url, String id, String key, String language) {
+ private static String formatUrl(String url, String key, String language) {
if (url == null) {
- url = "https://reverse.geocoder.ls.hereapi.com/6.2/reversegeocode.json";
+ url = "https://revgeocode.search.hereapi.com/v1/revgeocode";
}
- url += "?mode=retrieveAddresses&maxresults=1";
- url += "&prox=%f,%f,0";
- url += "&app_id=" + id;
- url += "&app_code=" + key;
+ url += "?types=address&limit=1";
+ url += "&at=%f,%f";
url += "&apiKey=" + key;
if (language != null) {
- url += "&language=" + language;
+ url += "&lang=" + language;
}
return url;
}
public HereGeocoder(
- Client client, String url, String id, String key, String language,
+ Client client, String url, String key, String language,
int cacheSize, AddressFormat addressFormat) {
- super(client, formatUrl(url, id, key, language), cacheSize, addressFormat);
+ super(client, formatUrl(url, key, language), cacheSize, addressFormat);
}
@Override
public Address parseAddress(JsonObject json) {
JsonObject result = json
- .getJsonObject("Response")
- .getJsonArray("View")
+ .getJsonArray("items")
.getJsonObject(0)
- .getJsonArray("Result")
- .getJsonObject(0)
- .getJsonObject("Location")
- .getJsonObject("Address");
+ .getJsonObject("address");
if (result != null) {
Address address = new Address();
- if (result.containsKey("Label")) {
- address.setFormattedAddress(result.getString("Label"));
+ if (result.containsKey("label")) {
+ address.setFormattedAddress(result.getString("label"));
}
- if (result.containsKey("HouseNumber")) {
- address.setHouse(result.getString("HouseNumber"));
+ if (result.containsKey("houseNumber")) {
+ address.setHouse(result.getString("houseNumber"));
}
- if (result.containsKey("Street")) {
- address.setStreet(result.getString("Street"));
+ if (result.containsKey("street")) {
+ address.setStreet(result.getString("street"));
}
- if (result.containsKey("City")) {
- address.setSettlement(result.getString("City"));
+ if (result.containsKey("city")) {
+ address.setSettlement(result.getString("city"));
}
- if (result.containsKey("District")) {
- address.setDistrict(result.getString("District"));
+ if (result.containsKey("district")) {
+ address.setDistrict(result.getString("district"));
}
- if (result.containsKey("State")) {
- address.setState(result.getString("State"));
+ if (result.containsKey("state")) {
+ address.setState(result.getString("state"));
}
- if (result.containsKey("Country")) {
- address.setCountry(result.getString("Country").toUpperCase());
+ if (result.containsKey("countryCode")) {
+ address.setCountry(result.getString("countryCode").toUpperCase());
}
- if (result.containsKey("PostalCode")) {
- address.setPostcode(result.getString("PostalCode"));
+ if (result.containsKey("postalCode")) {
+ address.setPostcode(result.getString("postalCode"));
}
return address;
diff --git a/src/main/java/org/traccar/geocoder/JsonGeocoder.java b/src/main/java/org/traccar/geocoder/JsonGeocoder.java
index 6105e8cfd..f9b039f43 100644
--- a/src/main/java/org/traccar/geocoder/JsonGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/JsonGeocoder.java
@@ -19,10 +19,10 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.database.StatisticsManager;
-import javax.json.JsonObject;
-import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.client.Client;
-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;
diff --git a/src/main/java/org/traccar/geocoder/LocationIqGeocoder.java b/src/main/java/org/traccar/geocoder/LocationIqGeocoder.java
index f2ffe02d6..f304ffeff 100644
--- a/src/main/java/org/traccar/geocoder/LocationIqGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/LocationIqGeocoder.java
@@ -15,7 +15,7 @@
*/
package org.traccar.geocoder;
-import javax.ws.rs.client.Client;
+import jakarta.ws.rs.client.Client;
public class LocationIqGeocoder extends NominatimGeocoder {
diff --git a/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java b/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java
index 3f2554c6e..1b6c8adcc 100644
--- a/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java
@@ -16,9 +16,9 @@
*/
package org.traccar.geocoder;
-import javax.json.JsonArray;
-import javax.json.JsonObject;
-import javax.ws.rs.client.Client;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.ws.rs.client.Client;
public class MapQuestGeocoder extends JsonGeocoder {
diff --git a/src/main/java/org/traccar/geocoder/MapTilerGeocoder.java b/src/main/java/org/traccar/geocoder/MapTilerGeocoder.java
index 203f5f99b..24c9da2ad 100644
--- a/src/main/java/org/traccar/geocoder/MapTilerGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/MapTilerGeocoder.java
@@ -15,9 +15,9 @@
*/
package org.traccar.geocoder;
-import javax.json.JsonArray;
-import javax.json.JsonObject;
-import javax.ws.rs.client.Client;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.ws.rs.client.Client;
public class MapTilerGeocoder extends JsonGeocoder {
diff --git a/src/main/java/org/traccar/geocoder/MapboxGeocoder.java b/src/main/java/org/traccar/geocoder/MapboxGeocoder.java
index 72bfb53f5..9fa6b8d88 100644
--- a/src/main/java/org/traccar/geocoder/MapboxGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/MapboxGeocoder.java
@@ -15,10 +15,10 @@
*/
package org.traccar.geocoder;
-import javax.json.JsonArray;
-import javax.json.JsonObject;
-import javax.json.JsonString;
-import javax.ws.rs.client.Client;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonString;
+import jakarta.ws.rs.client.Client;
public class MapboxGeocoder extends JsonGeocoder {
diff --git a/src/main/java/org/traccar/geocoder/MapmyIndiaGeocoder.java b/src/main/java/org/traccar/geocoder/MapmyIndiaGeocoder.java
index dea295cca..b68db07bc 100644
--- a/src/main/java/org/traccar/geocoder/MapmyIndiaGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/MapmyIndiaGeocoder.java
@@ -15,9 +15,9 @@
*/
package org.traccar.geocoder;
-import javax.json.JsonArray;
-import javax.json.JsonObject;
-import javax.ws.rs.client.Client;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.ws.rs.client.Client;
public class MapmyIndiaGeocoder extends JsonGeocoder {
diff --git a/src/main/java/org/traccar/geocoder/NominatimGeocoder.java b/src/main/java/org/traccar/geocoder/NominatimGeocoder.java
index b731549f7..1e26d0042 100644
--- a/src/main/java/org/traccar/geocoder/NominatimGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/NominatimGeocoder.java
@@ -15,8 +15,8 @@
*/
package org.traccar.geocoder;
-import javax.json.JsonObject;
-import javax.ws.rs.client.Client;
+import jakarta.json.JsonObject;
+import jakarta.ws.rs.client.Client;
public class NominatimGeocoder extends JsonGeocoder {
diff --git a/src/main/java/org/traccar/geocoder/OpenCageGeocoder.java b/src/main/java/org/traccar/geocoder/OpenCageGeocoder.java
index fb61440aa..4607fdc87 100644
--- a/src/main/java/org/traccar/geocoder/OpenCageGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/OpenCageGeocoder.java
@@ -16,9 +16,9 @@
*/
package org.traccar.geocoder;
-import javax.json.JsonArray;
-import javax.json.JsonObject;
-import javax.ws.rs.client.Client;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.ws.rs.client.Client;
public class OpenCageGeocoder extends JsonGeocoder {
diff --git a/src/main/java/org/traccar/geocoder/PositionStackGeocoder.java b/src/main/java/org/traccar/geocoder/PositionStackGeocoder.java
index 9778d9eda..4aed27fc5 100644
--- a/src/main/java/org/traccar/geocoder/PositionStackGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/PositionStackGeocoder.java
@@ -15,9 +15,9 @@
*/
package org.traccar.geocoder;
-import javax.json.JsonArray;
-import javax.json.JsonObject;
-import javax.ws.rs.client.Client;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.ws.rs.client.Client;
public class PositionStackGeocoder extends JsonGeocoder {
diff --git a/src/main/java/org/traccar/geocoder/TomTomGeocoder.java b/src/main/java/org/traccar/geocoder/TomTomGeocoder.java
index 9bb36efc2..4d452fd43 100644
--- a/src/main/java/org/traccar/geocoder/TomTomGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/TomTomGeocoder.java
@@ -15,9 +15,9 @@
*/
package org.traccar.geocoder;
-import javax.json.JsonArray;
-import javax.json.JsonObject;
-import javax.ws.rs.client.Client;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.ws.rs.client.Client;
public class TomTomGeocoder extends JsonGeocoder {
diff --git a/src/main/java/org/traccar/geolocation/GoogleGeolocationProvider.java b/src/main/java/org/traccar/geolocation/GoogleGeolocationProvider.java
index 8f0f3b704..9425e9111 100644
--- a/src/main/java/org/traccar/geolocation/GoogleGeolocationProvider.java
+++ b/src/main/java/org/traccar/geolocation/GoogleGeolocationProvider.java
@@ -15,7 +15,7 @@
*/
package org.traccar.geolocation;
-import javax.ws.rs.client.Client;
+import jakarta.ws.rs.client.Client;
public class GoogleGeolocationProvider extends UniversalGeolocationProvider {
diff --git a/src/main/java/org/traccar/geolocation/MozillaGeolocationProvider.java b/src/main/java/org/traccar/geolocation/MozillaGeolocationProvider.java
index 3b4ba4e1f..7eb22dcca 100644
--- a/src/main/java/org/traccar/geolocation/MozillaGeolocationProvider.java
+++ b/src/main/java/org/traccar/geolocation/MozillaGeolocationProvider.java
@@ -15,7 +15,7 @@
*/
package org.traccar.geolocation;
-import javax.ws.rs.client.Client;
+import jakarta.ws.rs.client.Client;
public class MozillaGeolocationProvider extends UniversalGeolocationProvider {
diff --git a/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java b/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java
index 82fcf42ab..72a05d10f 100644
--- a/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java
+++ b/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java
@@ -18,9 +18,9 @@ package org.traccar.geolocation;
import org.traccar.model.CellTower;
import org.traccar.model.Network;
-import javax.json.JsonObject;
-import javax.ws.rs.client.Client;
-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 {
diff --git a/src/main/java/org/traccar/geolocation/UniversalGeolocationProvider.java b/src/main/java/org/traccar/geolocation/UniversalGeolocationProvider.java
index 7a3f71ee1..9086d8ce3 100644
--- a/src/main/java/org/traccar/geolocation/UniversalGeolocationProvider.java
+++ b/src/main/java/org/traccar/geolocation/UniversalGeolocationProvider.java
@@ -17,10 +17,10 @@ package org.traccar.geolocation;
import org.traccar.model.Network;
-import javax.json.JsonObject;
-import javax.ws.rs.client.Client;
-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 {
diff --git a/src/main/java/org/traccar/geolocation/UnwiredGeolocationProvider.java b/src/main/java/org/traccar/geolocation/UnwiredGeolocationProvider.java
index 14893b6a3..4f1c5617e 100644
--- a/src/main/java/org/traccar/geolocation/UnwiredGeolocationProvider.java
+++ b/src/main/java/org/traccar/geolocation/UnwiredGeolocationProvider.java
@@ -23,10 +23,10 @@ import org.traccar.model.CellTower;
import org.traccar.model.Network;
import org.traccar.model.WifiAccessPoint;
-import javax.json.JsonObject;
-import javax.ws.rs.client.Client;
-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 {
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 620852502..8b010ceae 100644
--- a/src/main/java/org/traccar/handler/ComputedAttributesHandler.java
+++ b/src/main/java/org/traccar/handler/ComputedAttributesHandler.java
@@ -24,11 +24,15 @@ 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;
@@ -39,8 +43,8 @@ import org.traccar.model.Device;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
@Singleton
@ChannelHandler.Sharable
@@ -52,15 +56,33 @@ public class ComputedAttributesHandler extends BaseDataHandler {
private final JexlEngine engine;
+ private final JexlFeatures features;
+
private final boolean includeDeviceAttributes;
+ private final boolean includeLastAttributes;
@Inject
public ComputedAttributesHandler(Config config, CacheManager cacheManager) {
this.cacheManager = cacheManager;
- engine = new JexlEngine();
- engine.setStrict(true);
- engine.setFunctions(Collections.singletonMap("math", Math.class));
+ 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);
+ includeLastAttributes = config.getBoolean(Keys.PROCESSING_COMPUTED_ATTRIBUTES_LAST_ATTRIBUTES);
}
private MapContext prepareContext(Position position) {
@@ -68,13 +90,17 @@ public class ComputedAttributesHandler extends BaseDataHandler {
if (includeDeviceAttributes) {
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));
}
}
}
+ Position last = null;
+ if (includeLastAttributes) {
+ last = cacheManager.getPosition(position.getDeviceId());
+ }
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,9 +108,17 @@ public class ComputedAttributesHandler extends BaseDataHandler {
try {
if (!method.getReturnType().equals(Map.class)) {
result.set(name, method.invoke(position));
+ if (last != null) {
+ result.set(prefixAttribute("last", name), method.invoke(last));
+ }
} else {
- for (Object key : ((Map) method.invoke(position)).keySet()) {
- result.set((String) key, ((Map) method.invoke(position)).get(key));
+ for (Map.Entry<?, ?> entry : ((Map<?, ?>) method.invoke(position)).entrySet()) {
+ result.set((String) entry.getKey(), entry.getValue());
+ }
+ if (last != null) {
+ for (Map.Entry<?, ?> entry : ((Map<?, ?>) method.invoke(last)).entrySet()) {
+ result.set(prefixAttribute("last", (String) entry.getKey()), entry.getValue());
+ }
}
}
} catch (IllegalAccessException | InvocationTargetException error) {
@@ -95,12 +129,18 @@ public class ComputedAttributesHandler extends BaseDataHandler {
return result;
}
+ private String prefixAttribute(String prefix, String key) {
+ return prefix + Character.toUpperCase(key.charAt(0)) + key.substring(1);
+ }
+
/**
* @deprecated logic needs to be extracted to be used in API resource
*/
@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
diff --git a/src/main/java/org/traccar/handler/CopyAttributesHandler.java b/src/main/java/org/traccar/handler/CopyAttributesHandler.java
index e5c9bc29a..42b438e41 100644
--- a/src/main/java/org/traccar/handler/CopyAttributesHandler.java
+++ b/src/main/java/org/traccar/handler/CopyAttributesHandler.java
@@ -24,8 +24,8 @@ import org.traccar.helper.model.AttributeUtil;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
@Singleton
@ChannelHandler.Sharable
diff --git a/src/main/java/org/traccar/handler/DefaultDataHandler.java b/src/main/java/org/traccar/handler/DefaultDataHandler.java
index 89255a5fe..cca6dcd0a 100644
--- a/src/main/java/org/traccar/handler/DefaultDataHandler.java
+++ b/src/main/java/org/traccar/handler/DefaultDataHandler.java
@@ -24,8 +24,8 @@ import org.traccar.storage.Storage;
import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Request;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
@Singleton
@ChannelHandler.Sharable
diff --git a/src/main/java/org/traccar/handler/DistanceHandler.java b/src/main/java/org/traccar/handler/DistanceHandler.java
index 30dc9ff2b..db8c73779 100644
--- a/src/main/java/org/traccar/handler/DistanceHandler.java
+++ b/src/main/java/org/traccar/handler/DistanceHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2023 Anton Tananaev (anton@traccar.org)
* Copyright 2015 Amila Silva
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,6 +17,8 @@
package org.traccar.handler;
import io.netty.channel.ChannelHandler;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
import org.traccar.BaseDataHandler;
import org.traccar.config.Config;
import org.traccar.config.Keys;
@@ -24,11 +26,6 @@ import org.traccar.helper.DistanceCalculator;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-
@Singleton
@ChannelHandler.Sharable
public class DistanceHandler extends BaseDataHandler {
@@ -36,15 +33,15 @@ public class DistanceHandler extends BaseDataHandler {
private final CacheManager cacheManager;
private final boolean filter;
- private final int coordinatesMinError;
- private final int coordinatesMaxError;
+ private final int minError;
+ private final int maxError;
@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);
+ this.minError = config.getInteger(Keys.COORDINATES_MIN_ERROR);
+ this.maxError = config.getInteger(Keys.COORDINATES_MAX_ERROR);
}
@Override
@@ -54,8 +51,7 @@ public class DistanceHandler extends BaseDataHandler {
if (position.hasAttribute(Position.KEY_DISTANCE)) {
distance = position.getDouble(Position.KEY_DISTANCE);
}
- double totalDistance = 0.0;
-
+ double totalDistance;
Position last = cacheManager.getPosition(position.getDeviceId());
if (last != null) {
totalDistance = last.getDouble(Position.KEY_TOTAL_DISTANCE);
@@ -63,11 +59,10 @@ public class DistanceHandler extends BaseDataHandler {
distance = DistanceCalculator.distance(
position.getLatitude(), position.getLongitude(),
last.getLatitude(), last.getLongitude());
- distance = BigDecimal.valueOf(distance).setScale(2, RoundingMode.HALF_EVEN).doubleValue();
}
if (filter && last.getLatitude() != 0 && last.getLongitude() != 0) {
- boolean satisfiesMin = coordinatesMinError == 0 || distance > coordinatesMinError;
- boolean satisfiesMax = coordinatesMaxError == 0 || distance < coordinatesMaxError;
+ boolean satisfiesMin = minError == 0 || distance > minError;
+ boolean satisfiesMax = maxError == 0 || distance < maxError || position.getValid();
if (!satisfiesMin || !satisfiesMax) {
position.setValid(last.getValid());
position.setLatitude(last.getLatitude());
@@ -75,10 +70,11 @@ public class DistanceHandler extends BaseDataHandler {
distance = 0;
}
}
+ } else {
+ totalDistance = 0.0;
}
position.set(Position.KEY_DISTANCE, distance);
- totalDistance = BigDecimal.valueOf(totalDistance + distance).setScale(2, RoundingMode.HALF_EVEN).doubleValue();
- position.set(Position.KEY_TOTAL_DISTANCE, totalDistance);
+ position.set(Position.KEY_TOTAL_DISTANCE, totalDistance + distance);
return position;
}
diff --git a/src/main/java/org/traccar/handler/EngineHoursHandler.java b/src/main/java/org/traccar/handler/EngineHoursHandler.java
index c10fe9064..621205b34 100644
--- a/src/main/java/org/traccar/handler/EngineHoursHandler.java
+++ b/src/main/java/org/traccar/handler/EngineHoursHandler.java
@@ -21,8 +21,8 @@ import org.traccar.BaseDataHandler;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
@Singleton
@ChannelHandler.Sharable
diff --git a/src/main/java/org/traccar/handler/FilterHandler.java b/src/main/java/org/traccar/handler/FilterHandler.java
index 994276bb6..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 - 2022 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,13 +16,16 @@
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.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;
@@ -33,13 +36,13 @@ import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Order;
import org.traccar.storage.query.Request;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+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);
@@ -47,6 +50,7 @@ public class FilterHandler extends BaseDataHandler {
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;
@@ -55,19 +59,23 @@ public class FilterHandler extends BaseDataHandler {
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) {
+ 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);
@@ -76,11 +84,13 @@ public class FilterHandler extends BaseDataHandler {
filterDistance = config.getInteger(Keys.FILTER_DISTANCE);
filterMaxSpeed = config.getInteger(Keys.FILTER_MAX_SPEED);
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 {
@@ -114,6 +124,10 @@ 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;
}
@@ -158,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;
@@ -177,7 +198,7 @@ public class FilterHandler extends BaseDataHandler {
return false;
}
- private boolean filter(Position position) {
+ protected boolean filter(Position position) {
StringBuilder filterType = new StringBuilder();
@@ -188,6 +209,9 @@ public class FilterHandler extends BaseDataHandler {
if (filterZero(position)) {
filterType.append("Zero ");
}
+ if (filterOutdated(position)) {
+ filterType.append("Outdated ");
+ }
if (filterFuture(position)) {
filterType.append("Future ");
}
@@ -200,6 +224,9 @@ public class FilterHandler extends BaseDataHandler {
if (filterApproximate(position)) {
filterType.append("Approximate ");
}
+ if (filterDailyLimit(position)) {
+ filterType.append("DailyLimit ");
+ }
// filter out excessive data
long deviceId = position.getDeviceId();
@@ -233,9 +260,16 @@ public class FilterHandler extends BaseDataHandler {
}
}
+ 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 ");
+ }
+ }
+
if (filterType.length() > 0) {
- String uniqueId = cacheManager.getObject(Device.class, deviceId).getUniqueId();
- LOGGER.info("Position filtered by {}filters from device: {}", filterType, uniqueId);
+ LOGGER.info("Position filtered by {}filters from device: {}", filterType, device.getUniqueId());
return true;
}
@@ -243,11 +277,17 @@ public class FilterHandler extends BaseDataHandler {
}
@Override
- protected Position handlePosition(Position position) {
- if (enabled && 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/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 e7389f22d..a54ea03e3 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 - 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.
@@ -37,6 +37,7 @@ public class GeolocationHandler extends ChannelInboundHandlerAdapter {
private final StatisticsManager statisticsManager;
private final boolean processInvalidPositions;
private final boolean reuse;
+ private final boolean requireWifi;
public GeolocationHandler(
Config config, GeolocationProvider geolocationProvider, CacheManager cacheManager,
@@ -46,6 +47,7 @@ public class GeolocationHandler extends ChannelInboundHandlerAdapter {
this.statisticsManager = statisticsManager;
processInvalidPositions = config.getBoolean(Keys.GEOLOCATION_PROCESS_INVALID_POSITIONS);
reuse = config.getBoolean(Keys.GEOLOCATION_REUSE);
+ requireWifi = config.getBoolean(Keys.GEOLOCATION_REQUIRE_WIFI);
}
@Override
@@ -53,7 +55,8 @@ public class GeolocationHandler extends ChannelInboundHandlerAdapter {
if (message instanceof Position) {
final Position position = (Position) message;
if ((position.getOutdated() || processInvalidPositions && !position.getValid())
- && position.getNetwork() != null) {
+ && position.getNetwork() != null
+ && (!requireWifi || position.getNetwork().getWifiAccessPoints() != null)) {
if (reuse) {
Position lastPosition = cacheManager.getPosition(position.getDeviceId());
if (lastPosition != null && position.getNetwork().equals(lastPosition.getNetwork())) {
diff --git a/src/main/java/org/traccar/handler/HemisphereHandler.java b/src/main/java/org/traccar/handler/HemisphereHandler.java
index ccbde9fe5..294e449db 100644
--- a/src/main/java/org/traccar/handler/HemisphereHandler.java
+++ b/src/main/java/org/traccar/handler/HemisphereHandler.java
@@ -21,8 +21,8 @@ import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.model.Position;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
@Singleton
@ChannelHandler.Sharable
diff --git a/src/main/java/org/traccar/handler/MotionHandler.java b/src/main/java/org/traccar/handler/MotionHandler.java
index 10312f9b3..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 - 2022 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,27 +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.reports.common.TripsConfig;
+import org.traccar.session.cache.CacheManager;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
@Singleton
@ChannelHandler.Sharable
public class MotionHandler extends BaseDataHandler {
- private final double speedThreshold;
+ private final CacheManager cacheManager;
@Inject
- public MotionHandler(TripsConfig tripsConfig) {
- speedThreshold = tripsConfig.getSpeedThreshold();
+ public MotionHandler(CacheManager cacheManager) {
+ this.cacheManager = cacheManager;
}
@Override
protected Position handlePosition(Position position) {
if (!position.hasAttribute(Position.KEY_MOTION)) {
- position.set(Position.KEY_MOTION, position.getSpeed() > speedThreshold);
+ 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 e18d34ef2..61ada5b91 100644
--- a/src/main/java/org/traccar/handler/RemoteAddressHandler.java
+++ b/src/main/java/org/traccar/handler/RemoteAddressHandler.java
@@ -22,8 +22,8 @@ import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.model.Position;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
import java.net.InetSocketAddress;
@Singleton
diff --git a/src/main/java/org/traccar/handler/SpeedLimitHandler.java b/src/main/java/org/traccar/handler/SpeedLimitHandler.java
index 0c6025999..6edb6e912 100644
--- a/src/main/java/org/traccar/handler/SpeedLimitHandler.java
+++ b/src/main/java/org/traccar/handler/SpeedLimitHandler.java
@@ -23,8 +23,8 @@ import org.slf4j.LoggerFactory;
import org.traccar.model.Position;
import org.traccar.speedlimit.SpeedLimitProvider;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
@Singleton
@ChannelHandler.Sharable
diff --git a/src/main/java/org/traccar/handler/StandardLoggingHandler.java b/src/main/java/org/traccar/handler/StandardLoggingHandler.java
index 84492e2a5..5978d632e 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 - 2022 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,68 +20,73 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
+import jakarta.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.NetworkMessage;
import org.traccar.helper.NetworkUtil;
+import org.traccar.model.LogRecord;
+import org.traccar.session.ConnectionManager;
import java.net.InetSocketAddress;
-import java.net.SocketAddress;
public class StandardLoggingHandler extends ChannelDuplexHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(StandardLoggingHandler.class);
private final String protocol;
+ private ConnectionManager connectionManager;
public StandardLoggingHandler(String protocol) {
this.protocol = protocol;
}
+ @Inject
+ public void setConnectionManager(ConnectionManager connectionManager) {
+ this.connectionManager = connectionManager;
+ }
+
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
- log(ctx, false, msg);
+ LogRecord record = createLogRecord(msg);
+ log(ctx, false, record);
super.channelRead(ctx, msg);
+ if (record != null) {
+ connectionManager.updateLog(record);
+ }
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
- log(ctx, true, msg);
+ log(ctx, true, createLogRecord(msg));
super.write(ctx, msg, promise);
}
- public void log(ChannelHandlerContext ctx, boolean downstream, Object o) {
- if (o instanceof NetworkMessage) {
- NetworkMessage networkMessage = (NetworkMessage) o;
+ private LogRecord createLogRecord(Object msg) {
+ if (msg instanceof NetworkMessage) {
+ NetworkMessage networkMessage = (NetworkMessage) msg;
if (networkMessage.getMessage() instanceof ByteBuf) {
- log(ctx, downstream, networkMessage.getRemoteAddress(), (ByteBuf) networkMessage.getMessage());
+ LogRecord record = new LogRecord();
+ record.setAddress((InetSocketAddress) networkMessage.getRemoteAddress());
+ record.setProtocol(protocol);
+ record.setData(ByteBufUtil.hexDump((ByteBuf) networkMessage.getMessage()));
+ return record;
}
- } else if (o instanceof ByteBuf) {
- log(ctx, downstream, ctx.channel().remoteAddress(), (ByteBuf) o);
}
+ return null;
}
- public void log(ChannelHandlerContext ctx, boolean downstream, SocketAddress remoteAddress, ByteBuf buf) {
- StringBuilder message = new StringBuilder();
-
- message.append("[").append(NetworkUtil.session(ctx.channel())).append(": ");
- message.append(protocol);
- if (downstream) {
- message.append(" > ");
- } else {
- message.append(" < ");
+ private void log(ChannelHandlerContext ctx, boolean downstream, LogRecord record) {
+ if (record != null) {
+ StringBuilder message = new StringBuilder();
+ message.append("[").append(NetworkUtil.session(ctx.channel())).append(": ");
+ message.append(protocol);
+ message.append(downstream ? " > " : " < ");
+ message.append(record.getAddress().getHostString());
+ message.append("] ");
+ message.append(record.getData());
+ LOGGER.info(message.toString());
}
-
- if (remoteAddress instanceof InetSocketAddress) {
- message.append(((InetSocketAddress) remoteAddress).getHostString());
- } else {
- message.append("unknown");
- }
- message.append("] ");
-
- 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 c98b0bd4c..3c3e17450 100644
--- a/src/main/java/org/traccar/handler/TimeHandler.java
+++ b/src/main/java/org/traccar/handler/TimeHandler.java
@@ -23,8 +23,8 @@ import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.model.Position;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
diff --git a/src/main/java/org/traccar/handler/events/AlertEventHandler.java b/src/main/java/org/traccar/handler/events/AlertEventHandler.java
index 9f77df989..531a0f957 100644
--- a/src/main/java/org/traccar/handler/events/AlertEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/AlertEventHandler.java
@@ -25,8 +25,8 @@ import org.traccar.model.Event;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
@Singleton
@ChannelHandler.Sharable
diff --git a/src/main/java/org/traccar/handler/events/BaseEventHandler.java b/src/main/java/org/traccar/handler/events/BaseEventHandler.java
index 271aaa35d..4a4fb40ff 100644
--- a/src/main/java/org/traccar/handler/events/BaseEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/BaseEventHandler.java
@@ -22,7 +22,7 @@ import org.traccar.database.NotificationManager;
import org.traccar.model.Event;
import org.traccar.model.Position;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public abstract class BaseEventHandler extends BaseDataHandler {
diff --git a/src/main/java/org/traccar/handler/events/BehaviorEventHandler.java b/src/main/java/org/traccar/handler/events/BehaviorEventHandler.java
index 51bbd82d6..08ae35fcd 100644
--- a/src/main/java/org/traccar/handler/events/BehaviorEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/BehaviorEventHandler.java
@@ -23,8 +23,8 @@ import org.traccar.model.Event;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
import java.util.Collections;
import java.util.Map;
diff --git a/src/main/java/org/traccar/handler/events/CommandResultEventHandler.java b/src/main/java/org/traccar/handler/events/CommandResultEventHandler.java
index 772176e9c..b70f8f33b 100644
--- a/src/main/java/org/traccar/handler/events/CommandResultEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/CommandResultEventHandler.java
@@ -22,8 +22,8 @@ import io.netty.channel.ChannelHandler;
import org.traccar.model.Event;
import org.traccar.model.Position;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
@Singleton
@ChannelHandler.Sharable
diff --git a/src/main/java/org/traccar/handler/events/DriverEventHandler.java b/src/main/java/org/traccar/handler/events/DriverEventHandler.java
index 51fdc0307..b68327983 100644
--- a/src/main/java/org/traccar/handler/events/DriverEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/DriverEventHandler.java
@@ -22,8 +22,8 @@ import org.traccar.model.Event;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
import java.util.Collections;
import java.util.Map;
diff --git a/src/main/java/org/traccar/handler/events/FuelEventHandler.java b/src/main/java/org/traccar/handler/events/FuelEventHandler.java
index 462cc4223..e5085ecc2 100644
--- a/src/main/java/org/traccar/handler/events/FuelEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/FuelEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2022 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.
@@ -24,8 +24,8 @@ import org.traccar.model.Event;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
import java.util.Map;
@Singleton
@@ -60,13 +60,13 @@ public class FuelEventHandler extends BaseEventHandler {
if (change > 0) {
double threshold = AttributeUtil.lookup(
cacheManager, Keys.EVENT_FUEL_INCREASE_THRESHOLD, position.getDeviceId());
- if (change >= threshold) {
+ 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 (Math.abs(change) >= threshold) {
+ if (threshold > 0 && Math.abs(change) >= threshold) {
return Map.of(new Event(Event.TYPE_DEVICE_FUEL_DROP, position), position);
}
}
diff --git a/src/main/java/org/traccar/handler/events/GeofenceEventHandler.java b/src/main/java/org/traccar/handler/events/GeofenceEventHandler.java
index 9414f4b31..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 - 2022 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.
@@ -16,24 +16,15 @@
package org.traccar.handler.events;
import io.netty.channel.ChannelHandler;
-import org.traccar.config.Config;
-import org.traccar.helper.model.GeofenceUtil;
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.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 javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -43,62 +34,43 @@ import java.util.Map;
@ChannelHandler.Sharable
public class GeofenceEventHandler extends BaseEventHandler {
- private final Config config;
private final CacheManager cacheManager;
- private final ConnectionManager connectionManager;
- private final Storage storage;
@Inject
- public GeofenceEventHandler(
- Config config, CacheManager cacheManager, ConnectionManager connectionManager, Storage storage) {
- this.config = config;
+ public GeofenceEventHandler(CacheManager cacheManager) {
this.cacheManager = cacheManager;
- this.connectionManager = connectionManager;
- this.storage = storage;
}
@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) || !position.getValid()) {
+ if (!PositionUtil.isLatest(cacheManager, position)) {
return null;
}
- List<Long> currentGeofences = GeofenceUtil.getCurrentGeofences(config, cacheManager, 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);
-
-
- if (!oldGeofences.isEmpty() || !newGeofences.isEmpty()) {
- device.setGeofenceIds(currentGeofences.isEmpty() ? null : currentGeofences);
-
- try {
- storage.updateObject(device, new Request(
- new Columns.Include("geofenceIds"),
- new Condition.Equals("id", device.getId())));
- } catch (StorageException e) {
- throw new RuntimeException("Update device geofences error", e);
- }
- connectionManager.updateDevice(true, 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 = 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_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) {
diff --git a/src/main/java/org/traccar/handler/events/IgnitionEventHandler.java b/src/main/java/org/traccar/handler/events/IgnitionEventHandler.java
index b2e9a3325..ba4159a42 100644
--- a/src/main/java/org/traccar/handler/events/IgnitionEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/IgnitionEventHandler.java
@@ -26,8 +26,8 @@ import org.traccar.model.Event;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
@Singleton
@ChannelHandler.Sharable
diff --git a/src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java b/src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java
index 909950acf..2fa2e8869 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 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2024 Anton Tananaev (anton@traccar.org)
* Copyright 2016 - 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,8 +25,8 @@ import org.traccar.model.Maintenance;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
@Singleton
@ChannelHandler.Sharable
@@ -49,8 +49,8 @@ public class MaintenanceEventHandler extends BaseEventHandler {
Map<Event, Position> events = new HashMap<>();
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());
+ double oldValue = getValue(lastPosition, maintenance.getType());
+ double newValue = getValue(position, maintenance.getType());
if (oldValue != 0.0 && newValue != 0.0 && newValue >= maintenance.getStart()) {
if (oldValue < maintenance.getStart()
|| (long) ((oldValue - maintenance.getStart()) / maintenance.getPeriod())
@@ -67,4 +67,17 @@ public class MaintenanceEventHandler extends BaseEventHandler {
return events;
}
+ private double getValue(Position position, String type) {
+ switch (type) {
+ case "serverTime":
+ return position.getServerTime().getTime();
+ case "deviceTime":
+ return position.getDeviceTime().getTime();
+ case "fixTime":
+ return position.getFixTime().getTime();
+ default:
+ return position.getDouble(type);
+ }
+ }
+
}
diff --git a/src/main/java/org/traccar/handler/events/MediaEventHandler.java b/src/main/java/org/traccar/handler/events/MediaEventHandler.java
index a49e08e8d..52d8e6961 100644
--- a/src/main/java/org/traccar/handler/events/MediaEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/MediaEventHandler.java
@@ -19,8 +19,8 @@ import io.netty.channel.ChannelHandler;
import org.traccar.model.Event;
import org.traccar.model.Position;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
diff --git a/src/main/java/org/traccar/handler/events/MotionEventHandler.java b/src/main/java/org/traccar/handler/events/MotionEventHandler.java
index c406bd935..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 - 2022 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");
@@ -19,6 +19,8 @@ package org.traccar.handler.events;
import io.netty.channel.ChannelHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.traccar.config.Keys;
+import org.traccar.helper.model.AttributeUtil;
import org.traccar.helper.model.PositionUtil;
import org.traccar.model.Device;
import org.traccar.model.Event;
@@ -33,8 +35,8 @@ import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Request;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
import java.util.Collections;
import java.util.Map;
@@ -46,14 +48,11 @@ public class MotionEventHandler extends BaseEventHandler {
private final CacheManager cacheManager;
private final Storage storage;
- private final TripsConfig tripsConfig;
@Inject
- public MotionEventHandler(
- CacheManager cacheManager, Storage storage, TripsConfig tripsConfig) {
+ public MotionEventHandler(CacheManager cacheManager, Storage storage) {
this.cacheManager = cacheManager;
this.storage = storage;
- this.tripsConfig = tripsConfig;
}
@Override
@@ -61,14 +60,16 @@ public class MotionEventHandler extends BaseEventHandler {
long deviceId = position.getDeviceId();
Device device = cacheManager.getObject(Device.class, deviceId);
- if (device == null) {
+ if (device == null || !PositionUtil.isLatest(cacheManager, position)) {
return null;
}
- if (!PositionUtil.isLatest(cacheManager, position)
- || !tripsConfig.getProcessInvalidPositions() && !position.getValid()) {
+ boolean processInvalid = AttributeUtil.lookup(
+ cacheManager, Keys.EVENT_MOTION_PROCESS_INVALID_POSITIONS, deviceId);
+ if (!processInvalid && !position.getValid()) {
return null;
}
+ 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()) {
diff --git a/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java b/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java
index 4d6aa8857..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 - 2022 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");
@@ -36,8 +36,8 @@ import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Request;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
import java.util.Collections;
import java.util.Map;
@@ -52,6 +52,7 @@ public class OverspeedEventHandler extends BaseEventHandler {
private final long minimalDuration;
private final boolean preferLowest;
+ private final double multiplier;
@Inject
public OverspeedEventHandler(
@@ -60,6 +61,7 @@ public class OverspeedEventHandler extends BaseEventHandler {
this.storage = storage;
minimalDuration = config.getLong(Keys.EVENT_OVERSPEED_MINIMAL_DURATION) * 1000;
preferLowest = config.getBoolean(Keys.EVENT_OVERSPEED_PREFER_LOWEST);
+ multiplier = config.getDouble(Keys.EVENT_OVERSPEED_THRESHOLD_MULTIPLIER);
}
@Override
@@ -84,8 +86,8 @@ public class OverspeedEventHandler extends BaseEventHandler {
double geofenceSpeedLimit = 0;
long overspeedGeofenceId = 0;
- if (device.getGeofenceIds() != null) {
- for (long geofenceId : device.getGeofenceIds()) {
+ if (position.getGeofenceIds() != null) {
+ for (long geofenceId : position.getGeofenceIds()) {
Geofence geofence = cacheManager.getObject(Geofence.class, geofenceId);
if (geofence != null) {
double currentSpeedLimit = geofence.getDouble(Keys.EVENT_OVERSPEED_LIMIT.getKey());
@@ -107,7 +109,7 @@ public class OverspeedEventHandler extends BaseEventHandler {
}
OverspeedState state = OverspeedState.fromDevice(device);
- OverspeedProcessor.updateState(state, position, speedLimit, minimalDuration, overspeedGeofenceId);
+ OverspeedProcessor.updateState(state, position, speedLimit, multiplier, minimalDuration, overspeedGeofenceId);
if (state.isChanged()) {
state.toDevice(device);
try {
diff --git a/src/main/java/org/traccar/helper/BufferUtil.java b/src/main/java/org/traccar/helper/BufferUtil.java
index d1025f548..b453c437f 100644
--- a/src/main/java/org/traccar/helper/BufferUtil.java
+++ b/src/main/java/org/traccar/helper/BufferUtil.java
@@ -71,4 +71,20 @@ public final class BufferUtil {
}
}
+ 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;
+ }
+ }
+ return printable;
+ }
+
+ public static String readString(ByteBuf buf, int length) {
+ return buf.readCharSequence(length, StandardCharsets.US_ASCII).toString();
+ }
+
}
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
index b40e30d76..634950b85 100644
--- a/src/main/java/org/traccar/helper/ObjectMapperContextResolver.java
+++ b/src/main/java/org/traccar/helper/ObjectMapperContextResolver.java
@@ -17,8 +17,8 @@ package org.traccar.helper;
import com.fasterxml.jackson.databind.ObjectMapper;
-import javax.inject.Inject;
-import javax.ws.rs.ext.ContextResolver;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.ext.ContextResolver;
// This does not work as a lambda
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
diff --git a/src/main/java/org/traccar/helper/Parser.java b/src/main/java/org/traccar/helper/Parser.java
index aa39e1ad7..c2aea28fa 100644
--- a/src/main/java/org/traccar/helper/Parser.java
+++ b/src/main/java/org/traccar/helper/Parser.java
@@ -50,6 +50,17 @@ public class Parser {
public boolean hasNext(int number) {
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;
}
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
index 43558e8f7..2630f64f0 100644
--- a/src/main/java/org/traccar/helper/model/AttributeUtil.java
+++ b/src/main/java/org/traccar/helper/model/AttributeUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,25 +15,44 @@
*/
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() {
}
- @SuppressWarnings({ "deprecation", "unchecked" })
+ 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) {
- Device device = cacheManager.getObject(Device.class, 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 = cacheManager.getObject(Group.class, groupId);
+ Group group = provider.getGroup(groupId);
if (group != null) {
result = group.getAttributes().get(key.getKey());
groupId = group.getGroupId();
@@ -42,10 +61,10 @@ public final class AttributeUtil {
}
}
if (result == null && key.hasType(KeyType.SERVER)) {
- result = cacheManager.getServer().getAttributes().get(key.getKey());
+ result = provider.getServer().getAttributes().get(key.getKey());
}
if (result == null && key.hasType(KeyType.CONFIG)) {
- result = cacheManager.getConfig().getString(key.getKey());
+ result = provider.getConfig().getString(key.getKey());
}
if (result != null) {
@@ -91,4 +110,79 @@ public final class AttributeUtil {
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
index 597078caf..5d8cb5f25 100644
--- a/src/main/java/org/traccar/helper/model/DeviceUtil.java
+++ b/src/main/java/org/traccar/helper/model/DeviceUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -16,11 +16,20 @@
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() {
@@ -30,4 +39,41 @@ public final class DeviceUtil {
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/UserUtil.java b/src/main/java/org/traccar/helper/model/UserUtil.java
index 9f93afeae..4b1c404f9 100644
--- a/src/main/java/org/traccar/helper/model/UserUtil.java
+++ b/src/main/java/org/traccar/helper/model/UserUtil.java
@@ -15,6 +15,8 @@
*/
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;
@@ -23,6 +25,7 @@ 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 {
@@ -65,4 +68,11 @@ public final class UserUtil {
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
index b6b912d6c..90de3bcce 100644
--- a/src/main/java/org/traccar/mail/LogMailManager.java
+++ b/src/main/java/org/traccar/mail/LogMailManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -19,8 +19,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.model.User;
-import javax.mail.MessagingException;
-import javax.mail.internet.MimeBodyPart;
+import jakarta.mail.MessagingException;
+import jakarta.mail.internet.MimeBodyPart;
public class LogMailManager implements MailManager {
@@ -32,13 +32,17 @@ public class LogMailManager implements MailManager {
}
@Override
- public void sendMessage(User user, String subject, String body) throws MessagingException {
- sendMessage(user, subject, body, null);
+ 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, String subject, String body, MimeBodyPart attachment) throws MessagingException {
- LOGGER.info("\nTo: " + user.getEmail() + "\nSubject: " + subject + "\nBody:\n" + body);
+ 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
index 69efbed32..d05a07de9 100644
--- a/src/main/java/org/traccar/mail/MailManager.java
+++ b/src/main/java/org/traccar/mail/MailManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -17,15 +17,17 @@ package org.traccar.mail;
import org.traccar.model.User;
-import javax.mail.MessagingException;
-import javax.mail.internet.MimeBodyPart;
+import jakarta.mail.MessagingException;
+import jakarta.mail.internet.MimeBodyPart;
public interface MailManager {
boolean getEmailEnabled();
- void sendMessage(User user, String subject, String body) throws MessagingException;
+ void sendMessage(
+ User user, boolean system, String subject, String body) throws MessagingException;
- void sendMessage(User user, String subject, String body, MimeBodyPart attachment) 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
index 4a0b7048f..70099d879 100644
--- a/src/main/java/org/traccar/mail/SmtpMailManager.java
+++ b/src/main/java/org/traccar/mail/SmtpMailManager.java
@@ -23,16 +23,16 @@ import org.traccar.database.StatisticsManager;
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 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;
@@ -93,19 +93,21 @@ public final class SmtpMailManager implements MailManager {
return config.hasKey(Keys.MAIL_SMTP_HOST);
}
+ @Override
public void sendMessage(
- User user, String subject, String body) throws MessagingException {
- sendMessage(user, subject, body, null);
+ User user, boolean system, String subject, String body) throws MessagingException {
+ sendMessage(user, system, subject, body, null);
}
+ @Override
public void sendMessage(
- User user, String subject, String body, MimeBodyPart attachment) throws MessagingException {
+ 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) {
+ if (properties == null && (system || !config.getBoolean(Keys.MAIL_SMTP_SYSTEM_ONLY))) {
properties = getProperties(new PropertiesProvider(config));
}
if (properties == null) {
diff --git a/src/main/java/org/traccar/model/Calendar.java b/src/main/java/org/traccar/model/Calendar.java
index 62c51cc4a..03f1995ba 100644
--- a/src/main/java/org/traccar/model/Calendar.java
+++ b/src/main/java/org/traccar/model/Calendar.java
@@ -34,6 +34,7 @@ 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 {
@@ -68,16 +69,22 @@ public class Calendar extends ExtendedModel {
return calendar;
}
- public Collection<VEvent> findEvents(Date date) {
+ private Collection<VEvent> findEvents(Date date) {
if (calendar != null) {
- Period period = new Period(new DateTime(date), Duration.ZERO);
- Filter<VEvent> filter = new Filter<>(new PeriodRule<>(period));
+ 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();
}
}
+ 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 355594c64..4277cc4c4 100644
--- a/src/main/java/org/traccar/model/CellTower.java
+++ b/src/main/java/org/traccar/model/CellTower.java
@@ -104,7 +104,7 @@ public class CellTower {
}
public void setSignalStrength(Integer signalStrength) {
- this.signalStrength = signalStrength;
+ this.signalStrength = signalStrength > 0 ? -signalStrength : signalStrength;
}
public void setOperator(long operator) {
diff --git a/src/main/java/org/traccar/model/Device.java b/src/main/java/org/traccar/model/Device.java
index b8c87921d..a3088a613 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,16 +15,26 @@
*/
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;
import org.traccar.storage.StorageName;
+import java.util.Date;
+
@StorageName("tc_devices")
-public class Device extends GroupedModel implements Disableable {
+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;
@@ -43,7 +53,10 @@ public class Device extends GroupedModel implements Disableable {
}
public void setUniqueId(String uniqueId) {
- this.uniqueId = uniqueId;
+ if (uniqueId.contains("../") || uniqueId.contains("..\\")) {
+ throw new IllegalArgumentException("Invalid unique id");
+ }
+ this.uniqueId = uniqueId.trim();
}
public static final String STATUS_UNKNOWN = "unknown";
@@ -83,21 +96,6 @@ public class Device extends GroupedModel implements Disableable {
this.positionId = positionId;
}
- private List<Long> geofenceIds;
-
- @QueryIgnore
- 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;
- }
- }
-
private String phone;
public String getPhone() {
@@ -105,7 +103,7 @@ public class Device extends GroupedModel implements Disableable {
}
public void setPhone(String phone) {
- this.phone = phone;
+ this.phone = phone != null ? phone.trim() : null;
}
private String model;
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 0e851d748..6f90de9da 100644
--- a/src/main/java/org/traccar/model/Event.java
+++ b/src/main/java/org/traccar/model/Event.java
@@ -46,6 +46,7 @@ public class Event extends Message {
public static final String TYPE_DEVICE_UNKNOWN = "deviceUnknown";
public static final String TYPE_DEVICE_OFFLINE = "deviceOffline";
public static final String TYPE_DEVICE_INACTIVE = "deviceInactive";
+ public static final String TYPE_QUEUED_COMMAND_SENT = "queuedCommandSent";
public static final String TYPE_DEVICE_MOVING = "deviceMoving";
public static final String TYPE_DEVICE_STOPPED = "deviceStopped";
diff --git a/src/main/java/org/traccar/model/ExtendedModel.java b/src/main/java/org/traccar/model/ExtendedModel.java
index 7a61eda8c..d5cd094da 100644
--- a/src/main/java/org/traccar/model/ExtendedModel.java
+++ b/src/main/java/org/traccar/model/ExtendedModel.java
@@ -89,14 +89,19 @@ public class ExtendedModel extends BaseModel {
}
}
- public String getString(String key) {
+ public String getString(String key, String defaultValue) {
if (attributes.containsKey(key)) {
- return attributes.get(key).toString();
+ 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)) {
Object value = attributes.get(key);
diff --git a/src/main/java/org/traccar/model/Geofence.java b/src/main/java/org/traccar/model/Geofence.java
index 9259028fb..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 - 2022 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.
@@ -26,7 +26,19 @@ import org.traccar.storage.StorageName;
import java.text.ParseException;
@StorageName("tc_geofences")
-public class Geofence extends ScheduledModel {
+public class Geofence extends ExtendedModel implements Schedulable {
+
+ private long calendarId;
+
+ @Override
+ public long getCalendarId() {
+ return calendarId;
+ }
+
+ @Override
+ public void setCalendarId(long calendarId) {
+ this.calendarId = calendarId;
+ }
private String name;
diff --git a/src/main/java/org/traccar/model/LogRecord.java b/src/main/java/org/traccar/model/LogRecord.java
new file mode 100644
index 000000000..c19163af3
--- /dev/null
+++ b/src/main/java/org/traccar/model/LogRecord.java
@@ -0,0 +1,79 @@
+/*
+ * 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.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+import java.net.InetSocketAddress;
+
+public class LogRecord {
+
+ private InetSocketAddress address;
+
+ public void setAddress(InetSocketAddress address) {
+ this.address = address;
+ }
+
+ @JsonIgnore
+ public InetSocketAddress getAddress() {
+ return address;
+ }
+
+ public String getHost() {
+ return address.getHostString();
+ }
+
+ private String protocol;
+
+ public String getProtocol() {
+ return protocol;
+ }
+
+ public void setProtocol(String protocol) {
+ this.protocol = protocol;
+ }
+
+ private String uniqueId;
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ private long deviceId;
+
+ public long getDeviceId() {
+ return deviceId;
+ }
+
+ public void setDeviceId(long deviceId) {
+ this.deviceId = deviceId;
+ }
+
+ private String data;
+
+ public String getData() {
+ return data;
+ }
+
+ public void setData(String data) {
+ this.data = data;
+ }
+
+}
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/ObjectOperation.java b/src/main/java/org/traccar/model/ObjectOperation.java
new file mode 100644
index 000000000..b462580bb
--- /dev/null
+++ b/src/main/java/org/traccar/model/ObjectOperation.java
@@ -0,0 +1,7 @@
+package org.traccar.model;
+
+public enum ObjectOperation {
+ ADD,
+ UPDATE,
+ DELETE,
+}
diff --git a/src/main/java/org/traccar/model/Position.java b/src/main/java/org/traccar/model/Position.java
index 1286db5f2..39f63217d 100644
--- a/src/main/java/org/traccar/model/Position.java
+++ b/src/main/java/org/traccar/model/Position.java
@@ -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,20 +85,23 @@ 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
+ public static final String KEY_OBD_SPEED = "obdSpeed"; // km/h
public static final String KEY_OBD_ODOMETER = "obdOdometer"; // meters
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";
@@ -306,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/Report.java b/src/main/java/org/traccar/model/Report.java
index 1556ecc9e..2ee7ae288 100644
--- a/src/main/java/org/traccar/model/Report.java
+++ b/src/main/java/org/traccar/model/Report.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -18,7 +18,19 @@ package org.traccar.model;
import org.traccar.storage.StorageName;
@StorageName("tc_reports")
-public class Report extends ScheduledModel {
+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;
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 73645721b..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,6 +16,7 @@
package org.traccar.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
import org.traccar.storage.QueryIgnore;
import org.traccar.storage.StorageName;
@@ -227,6 +228,18 @@ public class Server extends ExtendedModel implements UserRestrictions {
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;
@@ -261,4 +274,27 @@ public class Server extends ExtendedModel implements UserRestrictions {
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 53594fe07..8cfee0f48 100644
--- a/src/main/java/org/traccar/model/User.java
+++ b/src/main/java/org/traccar/model/User.java
@@ -63,7 +63,7 @@ public class User extends ExtendedModel implements UserRestrictions, Disableable
}
public void setPhone(String phone) {
- this.phone = phone;
+ this.phone = phone != null ? phone.trim() : null;
}
private boolean readonly;
@@ -251,6 +251,26 @@ public class User extends ExtendedModel implements UserRestrictions, Disableable
this.poiLayer = poiLayer;
}
+ private String totpKey;
+
+ public String getTotpKey() {
+ return totpKey;
+ }
+
+ public void setTotpKey(String totpKey) {
+ this.totpKey = totpKey;
+ }
+
+ private boolean temporary;
+
+ public boolean getTemporary() {
+ return temporary;
+ }
+
+ public void setTemporary(boolean temporary) {
+ this.temporary = temporary;
+ }
+
@QueryIgnore
public String getPassword() {
return null;
diff --git a/src/main/java/org/traccar/model/WifiAccessPoint.java b/src/main/java/org/traccar/model/WifiAccessPoint.java
index e28c1b935..64858f4c7 100644
--- a/src/main/java/org/traccar/model/WifiAccessPoint.java
+++ b/src/main/java/org/traccar/model/WifiAccessPoint.java
@@ -52,7 +52,7 @@ public class WifiAccessPoint {
}
public void setSignalStrength(Integer signalStrength) {
- this.signalStrength = signalStrength;
+ this.signalStrength = signalStrength > 0 ? -signalStrength : signalStrength;
}
private Integer channel;
diff --git a/src/main/java/org/traccar/notification/NotificationFormatter.java b/src/main/java/org/traccar/notification/NotificationFormatter.java
index 9ee3b97b6..7685eac0d 100644
--- a/src/main/java/org/traccar/notification/NotificationFormatter.java
+++ b/src/main/java/org/traccar/notification/NotificationFormatter.java
@@ -19,16 +19,18 @@ package org.traccar.notification;
import org.apache.velocity.VelocityContext;
import org.traccar.helper.model.UserUtil;
import org.traccar.model.Device;
+import org.traccar.model.Driver;
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.Server;
import org.traccar.model.User;
import org.traccar.session.cache.CacheManager;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
@Singleton
public class NotificationFormatter {
@@ -43,13 +45,15 @@ public class NotificationFormatter {
this.textTemplateFormatter = textTemplateFormatter;
}
- public NotificationMessage formatMessage(User user, Event event, Position position, String templatePath) {
+ public NotificationMessage formatMessage(
+ Notification notification, User user, Event event, Position position, String templatePath) {
Server server = cacheManager.getServer();
Device device = cacheManager.getObject(Device.class, event.getDeviceId());
VelocityContext velocityContext = textTemplateFormatter.prepareContext(server, user);
+ velocityContext.put("notification", notification);
velocityContext.put("device", device);
velocityContext.put("event", event);
if (position != null) {
@@ -66,7 +70,8 @@ public class NotificationFormatter {
}
String driverUniqueId = event.getString(Position.KEY_DRIVER_UNIQUE_ID);
if (driverUniqueId != null) {
- velocityContext.put("driver", cacheManager.findDriverByUniqueId(device.getId(), driverUniqueId));
+ velocityContext.put("driver", cacheManager.getDeviceObjects(device.getId(), Driver.class).stream()
+ .filter(driver -> driver.getUniqueId().equals(driverUniqueId)).findFirst().orElse(null));
}
return textTemplateFormatter.formatMessage(velocityContext, event.getType(), templatePath);
diff --git a/src/main/java/org/traccar/notification/NotificationMessage.java b/src/main/java/org/traccar/notification/NotificationMessage.java
index 0fb8d7654..551a2d823 100644
--- a/src/main/java/org/traccar/notification/NotificationMessage.java
+++ b/src/main/java/org/traccar/notification/NotificationMessage.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2024 Anton Tananaev (anton@traccar.org)
* Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,12 +16,16 @@
*/
package org.traccar.notification;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
public class NotificationMessage {
- private String subject;
- private String body;
+ private final String subject;
+ private final String body;
- public NotificationMessage(String subject, String body) {
+ @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
+ public NotificationMessage(@JsonProperty("subject") String subject, @JsonProperty("body") String body) {
this.subject = subject;
this.body = body;
}
diff --git a/src/main/java/org/traccar/notification/NotificatorManager.java b/src/main/java/org/traccar/notification/NotificatorManager.java
index 1d9f4f423..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 - 2022 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");
@@ -17,23 +17,21 @@
package org.traccar.notification;
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.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.NotificatorPushover;
import org.traccar.notificators.NotificatorSms;
import org.traccar.notificators.NotificatorTelegram;
import org.traccar.notificators.NotificatorTraccar;
import org.traccar.notificators.NotificatorWeb;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
@@ -43,9 +41,8 @@ import java.util.stream.Collectors;
@Singleton
public class NotificatorManager {
- private static final Logger LOGGER = LoggerFactory.getLogger(NotificatorManager.class);
-
private static final Map<String, Class<? extends Notificator>> NOTIFICATORS_ALL = Map.of(
+ "command", NotificatorCommand.class,
"web", NotificatorWeb.class,
"mail", NotificatorMail.class,
"sms", NotificatorSms.class,
@@ -69,14 +66,13 @@ public class NotificatorManager {
public Notificator getNotificator(String type) {
var clazz = NOTIFICATORS_ALL.get(type);
- if (clazz != null) {
+ if (clazz != null && types.contains(type)) {
var notificator = injector.getInstance(clazz);
if (notificator != null) {
return notificator;
}
}
- LOGGER.warn("Failed to get notificator {}", type);
- return new NotificatorNull();
+ throw new RuntimeException("Failed to get notificator " + type);
}
public Set<Typed> getAllNotificatorTypes() {
diff --git a/src/main/java/org/traccar/notification/TextTemplateFormatter.java b/src/main/java/org/traccar/notification/TextTemplateFormatter.java
index 444f4a7c2..adcfc2ab5 100644
--- a/src/main/java/org/traccar/notification/TextTemplateFormatter.java
+++ b/src/main/java/org/traccar/notification/TextTemplateFormatter.java
@@ -15,10 +15,11 @@
*/
package org.traccar.notification;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
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;
@@ -29,8 +30,6 @@ import org.traccar.model.Server;
import org.traccar.model.User;
import org.traccar.storage.StorageException;
-import javax.inject.Inject;
-import javax.inject.Singleton;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
@@ -75,19 +74,8 @@ public class TextTemplateFormatter {
}
public Template getTemplate(String name, String path) {
-
- String templateFilePath;
- Template template;
-
- try {
- templateFilePath = Paths.get(path, name + ".vm").toString();
- 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 = velocityEngine.getTemplate(templateFilePath, StandardCharsets.UTF_8.name());
- }
- return template;
+ String templateFilePath = Paths.get(path, name + ".vm").toString();
+ return velocityEngine.getTemplate(templateFilePath, StandardCharsets.UTF_8.name());
}
public NotificationMessage formatMessage(VelocityContext velocityContext, String name, String templatePath) {
diff --git a/src/main/java/org/traccar/notificators/Notificator.java b/src/main/java/org/traccar/notificators/Notificator.java
index 052365c7a..59216b1b6 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 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 - 2024 Anton Tananaev (anton@traccar.org)
* Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,12 +17,30 @@
package org.traccar.notificators;
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;
-public interface Notificator {
+public abstract class Notificator {
- void send(User user, Event event, Position position) throws MessageException, InterruptedException;
+ private final NotificationFormatter notificationFormatter;
+ private final String templatePath;
+
+ public Notificator(NotificationFormatter notificationFormatter, String templatePath) {
+ this.notificationFormatter = notificationFormatter;
+ this.templatePath = templatePath;
+ }
+
+ public void send(Notification notification, User user, Event event, Position position) throws MessageException {
+ var message = notificationFormatter.formatMessage(notification, user, event, position, templatePath);
+ send(user, message, event, position);
+ }
+
+ public void send(User user, NotificationMessage message, Event event, Position position) throws MessageException {
+ throw new UnsupportedOperationException();
+ }
}
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..712dda274
--- /dev/null
+++ b/src/main/java/org/traccar/notificators/NotificatorCommand.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2023 - 2024 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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 extends Notificator {
+
+ private final Storage storage;
+ private final CommandsManager commandsManager;
+
+ @Inject
+ public NotificatorCommand(Storage storage, CommandsManager commandsManager) {
+ super(null, null);
+ 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 5ce2cbc0b..7440068f8 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 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 - 2024 Anton Tananaev (anton@traccar.org)
* Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,34 +24,50 @@ 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 com.google.firebase.messaging.Notification;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+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.ObjectOperation;
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.session.cache.CacheManager;
+import org.traccar.storage.Storage;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Request;
-import javax.inject.Inject;
-import javax.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 {
+public class NotificatorFirebase extends Notificator {
- private final NotificationFormatter notificationFormatter;
+ private static final Logger LOGGER = LoggerFactory.getLogger(NotificatorFirebase.class);
+ private final Storage storage;
+ private final CacheManager cacheManager;
@Inject
- public NotificatorFirebase(Config config, NotificationFormatter notificationFormatter) throws IOException {
+ public NotificatorFirebase(
+ Config config, NotificationFormatter notificationFormatter,
+ Storage storage, CacheManager cacheManager) throws IOException {
- this.notificationFormatter = notificationFormatter;
+ super(notificationFormatter, "short");
+ this.storage = storage;
+ this.cacheManager = cacheManager;
InputStream serviceAccount = new ByteArrayInputStream(
config.getString(Keys.NOTIFICATOR_FIREBASE_SERVICE_ACCOUNT).getBytes());
@@ -64,17 +80,16 @@ public class NotificatorFirebase implements Notificator {
}
@Override
- public void send(User user, Event event, Position position) throws MessageException {
+ public void send(User user, NotificationMessage message, 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("[, ]")));
- List<String> registrationTokens = Arrays.asList(user.getString("notificationTokens").split("[, ]"));
-
- MulticastMessage message = MulticastMessage.builder()
- .setNotification(Notification.builder()
- .setTitle(shortMessage.getSubject())
- .setBody(shortMessage.getBody())
+ var messageBuilder = MulticastMessage.builder()
+ .setNotification(com.google.firebase.messaging.Notification.builder()
+ .setTitle(message.getSubject())
+ .setBody(message.getBody())
.build())
.setAndroidConfig(AndroidConfig.builder()
.setNotification(AndroidNotification.builder()
@@ -86,19 +101,41 @@ public class NotificatorFirebase implements Notificator {
.setSound("default")
.build())
.build())
- .addAllTokens(registrationTokens)
- .putData("eventId", String.valueOf(event.getId()))
- .build();
+ .addAllTokens(registrationTokens);
+
+ if (event != null) {
+ messageBuilder.putData("eventId", String.valueOf(event.getId()));
+ }
try {
- var result = FirebaseMessaging.getInstance().sendMulticast(message);
- for (var response : result.getResponses()) {
+ var result = FirebaseMessaging.getInstance().sendEachForMulticast(messageBuilder.build());
+ List<String> failedTokens = new LinkedList<>();
+ var iterator = result.getResponses().listIterator();
+ while (iterator.hasNext()) {
+ int index = iterator.nextIndex();
+ var response = iterator.next();
if (!response.isSuccessful()) {
- throw new MessageException(response.getException());
+ 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());
+ }
+ }
+ 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.invalidateObject(true, User.class, user.getId(), ObjectOperation.UPDATE);
}
- } catch (FirebaseMessagingException e) {
- throw new MessageException(e);
+ } catch (Exception e) {
+ LOGGER.warn("Firebase error", e);
}
}
}
diff --git a/src/main/java/org/traccar/notificators/NotificatorMail.java b/src/main/java/org/traccar/notificators/NotificatorMail.java
index 19fde6756..e7ca10157 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 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2024 Anton Tananaev (anton@traccar.org)
* Copyright 2017 - 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,34 +16,32 @@
*/
package org.traccar.notificators;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+import jakarta.mail.MessagingException;
import org.traccar.mail.MailManager;
import org.traccar.model.Event;
import org.traccar.model.Position;
import org.traccar.model.User;
import org.traccar.notification.MessageException;
import org.traccar.notification.NotificationFormatter;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-import javax.mail.MessagingException;
+import org.traccar.notification.NotificationMessage;
@Singleton
-public class NotificatorMail implements Notificator {
+public class NotificatorMail extends Notificator {
private final MailManager mailManager;
- private final NotificationFormatter notificationFormatter;
@Inject
public NotificatorMail(MailManager mailManager, NotificationFormatter notificationFormatter) {
+ super(notificationFormatter, "full");
this.mailManager = mailManager;
- this.notificationFormatter = notificationFormatter;
}
@Override
- public void send(User user, Event event, Position position) throws MessageException {
+ public void send(User user, NotificationMessage message, Event event, Position position) throws MessageException {
try {
- var fullMessage = notificationFormatter.formatMessage(user, event, position, "full");
- mailManager.sendMessage(user, fullMessage.getSubject(), fullMessage.getBody());
+ mailManager.sendMessage(user, false, message.getSubject(), message.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 ba9ade9c6..000000000
--- a/src/main/java/org/traccar/notificators/NotificatorNull.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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");
- * 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;
-import org.traccar.model.User;
-
-public class NotificatorNull implements Notificator {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(NotificatorNull.class);
-
- @Override
- public void send(User user, 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 e00db0579..a9708818b 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 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2020 - 2024 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,22 +16,21 @@
package org.traccar.notificators;
import com.fasterxml.jackson.annotation.JsonProperty;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.Entity;
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 javax.inject.Inject;
-import javax.inject.Singleton;
-import javax.ws.rs.client.Client;
-import javax.ws.rs.client.Entity;
+import org.traccar.notification.NotificationMessage;
@Singleton
-public class NotificatorPushover implements Notificator {
+public class NotificatorPushover extends Notificator {
- private final NotificationFormatter notificationFormatter;
private final Client client;
private final String url;
@@ -53,7 +52,7 @@ public class NotificatorPushover implements Notificator {
@Inject
public NotificatorPushover(Config config, NotificationFormatter notificationFormatter, Client client) {
- this.notificationFormatter = notificationFormatter;
+ super(notificationFormatter, "short");
this.client = client;
url = "https://api.pushover.net/1/messages.json";
token = config.getString(Keys.NOTIFICATOR_PUSHOVER_TOKEN);
@@ -61,8 +60,7 @@ public class NotificatorPushover implements Notificator {
}
@Override
- public void send(User user, Event event, Position position) {
- var shortMessage = notificationFormatter.formatMessage(user, event, position, "short");
+ public void send(User user, NotificationMessage shortMessage, Event event, Position position) {
Message message = new Message();
message.token = token;
diff --git a/src/main/java/org/traccar/notificators/NotificatorSms.java b/src/main/java/org/traccar/notificators/NotificatorSms.java
index e37d10888..a8086adcc 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 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2024 Anton Tananaev (anton@traccar.org)
* Copyright 2017 - 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,38 +16,36 @@
*/
package org.traccar.notificators;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
import org.traccar.database.StatisticsManager;
import org.traccar.model.Event;
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;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
@Singleton
-public class NotificatorSms implements Notificator {
+public class NotificatorSms extends Notificator {
private final SmsManager smsManager;
- private final NotificationFormatter notificationFormatter;
private final StatisticsManager statisticsManager;
@Inject
public NotificatorSms(
SmsManager smsManager, NotificationFormatter notificationFormatter, StatisticsManager statisticsManager) {
+ super(notificationFormatter, "short");
this.smsManager = smsManager;
- this.notificationFormatter = notificationFormatter;
this.statisticsManager = statisticsManager;
}
@Override
- public void send(User user, Event event, Position position) throws MessageException, InterruptedException {
+ public void send(User user, NotificationMessage message, Event event, Position position) throws MessageException {
if (user.getPhone() != null) {
- var shortMessage = notificationFormatter.formatMessage(user, event, position, "short");
statisticsManager.registerSms();
- smsManager.sendMessage(user.getPhone(), shortMessage.getBody(), false);
+ smsManager.sendMessage(user.getPhone(), message.getBody(), false);
}
}
diff --git a/src/main/java/org/traccar/notificators/NotificatorTelegram.java b/src/main/java/org/traccar/notificators/NotificatorTelegram.java
index 38e87c222..4c8d7b8aa 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 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2024 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,21 @@
package org.traccar.notificators;
import com.fasterxml.jackson.annotation.JsonProperty;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.Entity;
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 javax.inject.Inject;
-import javax.inject.Singleton;
-import javax.ws.rs.client.Client;
-import javax.ws.rs.client.Entity;
+import org.traccar.notification.NotificationMessage;
@Singleton
-public class NotificatorTelegram implements Notificator {
+public class NotificatorTelegram extends Notificator {
- private final NotificationFormatter notificationFormatter;
private final Client client;
private final String urlSendText;
@@ -64,7 +63,7 @@ public class NotificatorTelegram implements Notificator {
@Inject
public NotificatorTelegram(Config config, NotificationFormatter notificationFormatter, Client client) {
- this.notificationFormatter = notificationFormatter;
+ super(notificationFormatter, "short");
this.client = client;
urlSendText = String.format(
"https://api.telegram.org/bot%s/sendMessage", config.getString(Keys.NOTIFICATOR_TELEGRAM_KEY));
@@ -85,8 +84,7 @@ public class NotificatorTelegram implements Notificator {
}
@Override
- public void send(User user, Event event, Position position) {
- var shortMessage = notificationFormatter.formatMessage(user, event, position, "short");
+ public void send(User user, NotificationMessage shortMessage, Event event, Position position) {
TextMessage message = new TextMessage();
message.chatId = user.getString("telegramChatId");
diff --git a/src/main/java/org/traccar/notificators/NotificatorTraccar.java b/src/main/java/org/traccar/notificators/NotificatorTraccar.java
index 9ae39f975..983c4cda6 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 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2020 - 2024 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,28 +16,46 @@
package org.traccar.notificators;
import com.fasterxml.jackson.annotation.JsonProperty;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.model.ObjectOperation;
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.notification.NotificationMessage;
+import org.traccar.session.cache.CacheManager;
+import org.traccar.storage.Storage;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Request;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-import javax.ws.rs.client.Client;
-import javax.ws.rs.client.Entity;
+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;
@Singleton
-public class NotificatorTraccar implements Notificator {
+public class NotificatorTraccar extends 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 Notification {
+ public static class NotificationObject {
@JsonProperty("title")
private String title;
@JsonProperty("body")
@@ -50,33 +68,69 @@ public class NotificatorTraccar implements Notificator {
@JsonProperty("registration_ids")
private String[] tokens;
@JsonProperty("notification")
- private Notification notification;
+ private NotificationObject notification;
}
@Inject
- public NotificatorTraccar(Config config, NotificationFormatter notificationFormatter, Client client) {
- this.notificationFormatter = notificationFormatter;
+ public NotificatorTraccar(
+ Config config, NotificationFormatter notificationFormatter, Client client,
+ Storage storage, CacheManager cacheManager) {
+ super(notificationFormatter, "short");
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(User user, Event event, Position position) {
+ public void send(User user, NotificationMessage shortMessage, 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";
- Notification notification = new Notification();
- notification.title = shortMessage.getSubject();
- notification.body = shortMessage.getBody();
- notification.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 = notification;
+ message.notification = item;
- client.target(url).request().header("Authorization", "key=" + key).post(Entity.json(message)).close();
+ 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.invalidateObject(true, User.class, user.getId(), ObjectOperation.UPDATE);
+ }
+ } catch (Exception 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 deabeade1..b7bbdac1b 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 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 - 2024 Anton Tananaev (anton@traccar.org)
* Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,29 +17,30 @@
package org.traccar.notificators;
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;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
@Singleton
-public final class NotificatorWeb implements Notificator {
+public final class NotificatorWeb extends Notificator {
private final ConnectionManager connectionManager;
private final NotificationFormatter notificationFormatter;
@Inject
- public NotificatorWeb(
- ConnectionManager connectionManager, NotificationFormatter notificationFormatter) {
+ public NotificatorWeb(ConnectionManager connectionManager, NotificationFormatter notificationFormatter) {
+ super(null, null);
this.connectionManager = connectionManager;
this.notificationFormatter = notificationFormatter;
}
@Override
- public void send(User user, Event event, Position position) {
+ public void send(Notification notification, User user, Event event, Position position) {
Event copy = new Event();
copy.setId(event.getId());
@@ -51,7 +52,7 @@ public final class NotificatorWeb implements Notificator {
copy.setMaintenanceId(event.getMaintenanceId());
copy.getAttributes().putAll(event.getAttributes());
- var message = notificationFormatter.formatMessage(user, event, position, "short");
+ var message = notificationFormatter.formatMessage(notification, 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 bab1d2339..3856dc906 100644
--- a/src/main/java/org/traccar/protocol/AdmProtocol.java
+++ b/src/main/java/org/traccar/protocol/AdmProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class AdmProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/AisProtocol.java b/src/main/java/org/traccar/protocol/AisProtocol.java
index bc975c277..e792a1d3f 100644
--- a/src/main/java/org/traccar/protocol/AisProtocol.java
+++ b/src/main/java/org/traccar/protocol/AisProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class AisProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/AlematicsProtocol.java b/src/main/java/org/traccar/protocol/AlematicsProtocol.java
index b85b44382..5219607e7 100644
--- a/src/main/java/org/traccar/protocol/AlematicsProtocol.java
+++ b/src/main/java/org/traccar/protocol/AlematicsProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class AlematicsProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/AnytrekProtocol.java b/src/main/java/org/traccar/protocol/AnytrekProtocol.java
index b0e974c69..5dce15ce1 100644
--- a/src/main/java/org/traccar/protocol/AnytrekProtocol.java
+++ b/src/main/java/org/traccar/protocol/AnytrekProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.config.Config;
import java.nio.ByteOrder;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class AnytrekProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/ApelProtocol.java b/src/main/java/org/traccar/protocol/ApelProtocol.java
index f1d6e659c..550581b85 100644
--- a/src/main/java/org/traccar/protocol/ApelProtocol.java
+++ b/src/main/java/org/traccar/protocol/ApelProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import java.nio.ByteOrder;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class ApelProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/AplicomProtocol.java b/src/main/java/org/traccar/protocol/AplicomProtocol.java
index 47bb780cb..48c628d22 100644
--- a/src/main/java/org/traccar/protocol/AplicomProtocol.java
+++ b/src/main/java/org/traccar/protocol/AplicomProtocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class AplicomProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/AppelloProtocol.java b/src/main/java/org/traccar/protocol/AppelloProtocol.java
index 25b2bf3b8..34055d7e4 100644
--- a/src/main/java/org/traccar/protocol/AppelloProtocol.java
+++ b/src/main/java/org/traccar/protocol/AppelloProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class AppelloProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/AquilaProtocol.java b/src/main/java/org/traccar/protocol/AquilaProtocol.java
index 6080df33d..bd9c34d08 100644
--- a/src/main/java/org/traccar/protocol/AquilaProtocol.java
+++ b/src/main/java/org/traccar/protocol/AquilaProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class AquilaProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Ardi01Protocol.java b/src/main/java/org/traccar/protocol/Ardi01Protocol.java
index b33c2817f..5ddbe9d62 100644
--- a/src/main/java/org/traccar/protocol/Ardi01Protocol.java
+++ b/src/main/java/org/traccar/protocol/Ardi01Protocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Ardi01Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/ArknavProtocol.java b/src/main/java/org/traccar/protocol/ArknavProtocol.java
index 4f443aa3a..20fec296c 100644
--- a/src/main/java/org/traccar/protocol/ArknavProtocol.java
+++ b/src/main/java/org/traccar/protocol/ArknavProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class ArknavProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/ArknavX8Protocol.java b/src/main/java/org/traccar/protocol/ArknavX8Protocol.java
index 39c6e8009..a8be6f40c 100644
--- a/src/main/java/org/traccar/protocol/ArknavX8Protocol.java
+++ b/src/main/java/org/traccar/protocol/ArknavX8Protocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class ArknavX8Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/ArmoliProtocol.java b/src/main/java/org/traccar/protocol/ArmoliProtocol.java
index 32fba3b52..e9c947ecd 100644
--- a/src/main/java/org/traccar/protocol/ArmoliProtocol.java
+++ b/src/main/java/org/traccar/protocol/ArmoliProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class ArmoliProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/ArnaviProtocol.java b/src/main/java/org/traccar/protocol/ArnaviProtocol.java
index 091d5c06f..962bcce52 100644
--- a/src/main/java/org/traccar/protocol/ArnaviProtocol.java
+++ b/src/main/java/org/traccar/protocol/ArnaviProtocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class ArnaviProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java b/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java
index 361eeeef2..50ceea898 100644
--- a/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java
@@ -21,7 +21,7 @@ import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.Protocol;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.net.SocketAddress;
public class ArnaviProtocolDecoder extends BaseProtocolDecoder {
diff --git a/src/main/java/org/traccar/protocol/AstraProtocol.java b/src/main/java/org/traccar/protocol/AstraProtocol.java
index 021a81e07..dcc02d10d 100644
--- a/src/main/java/org/traccar/protocol/AstraProtocol.java
+++ b/src/main/java/org/traccar/protocol/AstraProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class AstraProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/At2000Protocol.java b/src/main/java/org/traccar/protocol/At2000Protocol.java
index 25e9be86f..c7e22f142 100644
--- a/src/main/java/org/traccar/protocol/At2000Protocol.java
+++ b/src/main/java/org/traccar/protocol/At2000Protocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class At2000Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/AtrackProtocol.java b/src/main/java/org/traccar/protocol/AtrackProtocol.java
index 21eb09696..8b86955f4 100644
--- a/src/main/java/org/traccar/protocol/AtrackProtocol.java
+++ b/src/main/java/org/traccar/protocol/AtrackProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class AtrackProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java
index 340641729..8896dcfb0 100644
--- a/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java
@@ -429,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;
}
diff --git a/src/main/java/org/traccar/protocol/AuroProtocol.java b/src/main/java/org/traccar/protocol/AuroProtocol.java
index d37884c8b..728c8e23c 100644
--- a/src/main/java/org/traccar/protocol/AuroProtocol.java
+++ b/src/main/java/org/traccar/protocol/AuroProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class AuroProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/AustinNbProtocol.java b/src/main/java/org/traccar/protocol/AustinNbProtocol.java
index 6a68467e2..467deff53 100644
--- a/src/main/java/org/traccar/protocol/AustinNbProtocol.java
+++ b/src/main/java/org/traccar/protocol/AustinNbProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class AustinNbProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/AutoFonProtocol.java b/src/main/java/org/traccar/protocol/AutoFonProtocol.java
index 0566b1da6..75bd28d5e 100644
--- a/src/main/java/org/traccar/protocol/AutoFonProtocol.java
+++ b/src/main/java/org/traccar/protocol/AutoFonProtocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class AutoFonProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/AutoGradeProtocol.java b/src/main/java/org/traccar/protocol/AutoGradeProtocol.java
index bc80e473a..69d9fb4e6 100644
--- a/src/main/java/org/traccar/protocol/AutoGradeProtocol.java
+++ b/src/main/java/org/traccar/protocol/AutoGradeProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class AutoGradeProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/AutoTrackProtocol.java b/src/main/java/org/traccar/protocol/AutoTrackProtocol.java
index 80255d3e9..df489de3c 100644
--- a/src/main/java/org/traccar/protocol/AutoTrackProtocol.java
+++ b/src/main/java/org/traccar/protocol/AutoTrackProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import java.nio.ByteOrder;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class AutoTrackProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/AvemaProtocol.java b/src/main/java/org/traccar/protocol/AvemaProtocol.java
index b35a447ff..0eeee41b8 100644
--- a/src/main/java/org/traccar/protocol/AvemaProtocol.java
+++ b/src/main/java/org/traccar/protocol/AvemaProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class AvemaProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Avl301Protocol.java b/src/main/java/org/traccar/protocol/Avl301Protocol.java
index c4a0affdc..452bc1501 100644
--- a/src/main/java/org/traccar/protocol/Avl301Protocol.java
+++ b/src/main/java/org/traccar/protocol/Avl301Protocol.java
@@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Avl301Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/B2316Protocol.java b/src/main/java/org/traccar/protocol/B2316Protocol.java
index 582be0b56..e0a6821d8 100644
--- a/src/main/java/org/traccar/protocol/B2316Protocol.java
+++ b/src/main/java/org/traccar/protocol/B2316Protocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class B2316Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/B2316ProtocolDecoder.java b/src/main/java/org/traccar/protocol/B2316ProtocolDecoder.java
index 635806b2d..b0a5411f7 100644
--- a/src/main/java/org/traccar/protocol/B2316ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/B2316ProtocolDecoder.java
@@ -24,9 +24,9 @@ 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;
diff --git a/src/main/java/org/traccar/protocol/BceProtocol.java b/src/main/java/org/traccar/protocol/BceProtocol.java
index 31fb1bd83..5e1c10abc 100644
--- a/src/main/java/org/traccar/protocol/BceProtocol.java
+++ b/src/main/java/org/traccar/protocol/BceProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class BceProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/BlackKiteProtocol.java b/src/main/java/org/traccar/protocol/BlackKiteProtocol.java
index 3859a9273..d584af5a1 100644
--- a/src/main/java/org/traccar/protocol/BlackKiteProtocol.java
+++ b/src/main/java/org/traccar/protocol/BlackKiteProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class BlackKiteProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/BlueProtocol.java b/src/main/java/org/traccar/protocol/BlueProtocol.java
index da195f438..821111ad7 100644
--- a/src/main/java/org/traccar/protocol/BlueProtocol.java
+++ b/src/main/java/org/traccar/protocol/BlueProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class BlueProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/BoxProtocol.java b/src/main/java/org/traccar/protocol/BoxProtocol.java
index dc6852d50..ac1ba7cae 100644
--- a/src/main/java/org/traccar/protocol/BoxProtocol.java
+++ b/src/main/java/org/traccar/protocol/BoxProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class BoxProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/BstplProtocol.java b/src/main/java/org/traccar/protocol/BstplProtocol.java
index dde14a2ca..ccedcf3d9 100644
--- a/src/main/java/org/traccar/protocol/BstplProtocol.java
+++ b/src/main/java/org/traccar/protocol/BstplProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class BstplProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/C2stekProtocol.java b/src/main/java/org/traccar/protocol/C2stekProtocol.java
index 5cd8ef4fd..5370ea762 100644
--- a/src/main/java/org/traccar/protocol/C2stekProtocol.java
+++ b/src/main/java/org/traccar/protocol/C2stekProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class C2stekProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/CalAmpProtocol.java b/src/main/java/org/traccar/protocol/CalAmpProtocol.java
index d67308cf2..06df6e196 100644
--- a/src/main/java/org/traccar/protocol/CalAmpProtocol.java
+++ b/src/main/java/org/traccar/protocol/CalAmpProtocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class CalAmpProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/CarTrackProtocol.java b/src/main/java/org/traccar/protocol/CarTrackProtocol.java
index 0538aad72..366f32034 100644
--- a/src/main/java/org/traccar/protocol/CarTrackProtocol.java
+++ b/src/main/java/org/traccar/protocol/CarTrackProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class CarTrackProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/CarcellProtocol.java b/src/main/java/org/traccar/protocol/CarcellProtocol.java
index 832d9bb2d..7ae8159d5 100644
--- a/src/main/java/org/traccar/protocol/CarcellProtocol.java
+++ b/src/main/java/org/traccar/protocol/CarcellProtocol.java
@@ -24,7 +24,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class CarcellProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/CarscopProtocol.java b/src/main/java/org/traccar/protocol/CarscopProtocol.java
index a4413af28..e904c01c5 100644
--- a/src/main/java/org/traccar/protocol/CarscopProtocol.java
+++ b/src/main/java/org/traccar/protocol/CarscopProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class CarscopProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/CastelProtocol.java b/src/main/java/org/traccar/protocol/CastelProtocol.java
index 9323b1503..74a9e9ca1 100644
--- a/src/main/java/org/traccar/protocol/CastelProtocol.java
+++ b/src/main/java/org/traccar/protocol/CastelProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.config.Config;
import org.traccar.model.Command;
import java.nio.ByteOrder;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class CastelProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/CastelProtocolDecoder.java b/src/main/java/org/traccar/protocol/CastelProtocolDecoder.java
index 4aa65245b..b076b9f66 100644
--- a/src/main/java/org/traccar/protocol/CastelProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/CastelProtocolDecoder.java
@@ -16,7 +16,6 @@
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;
@@ -284,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;
}
@@ -359,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
@@ -421,12 +432,26 @@ 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()));
+ }
+ codes.append(' ');
+ }
+ position.set(Position.KEY_DTCS, codes.toString().trim());
return position;
diff --git a/src/main/java/org/traccar/protocol/CautelaProtocol.java b/src/main/java/org/traccar/protocol/CautelaProtocol.java
index d0ca35ef1..067345f49 100644
--- a/src/main/java/org/traccar/protocol/CautelaProtocol.java
+++ b/src/main/java/org/traccar/protocol/CautelaProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class CautelaProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/CellocatorProtocol.java b/src/main/java/org/traccar/protocol/CellocatorProtocol.java
index 3287928c7..e3325c8b7 100644
--- a/src/main/java/org/traccar/protocol/CellocatorProtocol.java
+++ b/src/main/java/org/traccar/protocol/CellocatorProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class CellocatorProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/CguardProtocol.java b/src/main/java/org/traccar/protocol/CguardProtocol.java
index caf0aad42..c0fc9582a 100644
--- a/src/main/java/org/traccar/protocol/CguardProtocol.java
+++ b/src/main/java/org/traccar/protocol/CguardProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class CguardProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/CityeasyProtocol.java b/src/main/java/org/traccar/protocol/CityeasyProtocol.java
index 9656b284b..60ed5d135 100644
--- a/src/main/java/org/traccar/protocol/CityeasyProtocol.java
+++ b/src/main/java/org/traccar/protocol/CityeasyProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class CityeasyProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/ContinentalProtocol.java b/src/main/java/org/traccar/protocol/ContinentalProtocol.java
index 06e93d79d..9567374fd 100644
--- a/src/main/java/org/traccar/protocol/ContinentalProtocol.java
+++ b/src/main/java/org/traccar/protocol/ContinentalProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class ContinentalProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/CradlepointProtocol.java b/src/main/java/org/traccar/protocol/CradlepointProtocol.java
index 7f201a31d..220db0747 100644
--- a/src/main/java/org/traccar/protocol/CradlepointProtocol.java
+++ b/src/main/java/org/traccar/protocol/CradlepointProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class CradlepointProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/DingtekProtocol.java b/src/main/java/org/traccar/protocol/DingtekProtocol.java
index e9466b7e8..ab3e32fdb 100644
--- a/src/main/java/org/traccar/protocol/DingtekProtocol.java
+++ b/src/main/java/org/traccar/protocol/DingtekProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class DingtekProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/DishaProtocol.java b/src/main/java/org/traccar/protocol/DishaProtocol.java
index f83b8349a..0a582731d 100644
--- a/src/main/java/org/traccar/protocol/DishaProtocol.java
+++ b/src/main/java/org/traccar/protocol/DishaProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class DishaProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/DmtHttpProtocol.java b/src/main/java/org/traccar/protocol/DmtHttpProtocol.java
index 0dab26cda..d15bfa1ca 100644
--- a/src/main/java/org/traccar/protocol/DmtHttpProtocol.java
+++ b/src/main/java/org/traccar/protocol/DmtHttpProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class DmtHttpProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/DmtHttpProtocolDecoder.java b/src/main/java/org/traccar/protocol/DmtHttpProtocolDecoder.java
index 807850778..c2e617a2a 100644
--- a/src/main/java/org/traccar/protocol/DmtHttpProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/DmtHttpProtocolDecoder.java
@@ -25,9 +25,9 @@ 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 de56c9372..e89920cd3 100644
--- a/src/main/java/org/traccar/protocol/DmtProtocol.java
+++ b/src/main/java/org/traccar/protocol/DmtProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import java.nio.ByteOrder;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class DmtProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/DolphinProtocol.java b/src/main/java/org/traccar/protocol/DolphinProtocol.java
index ed627be78..e2acce7dd 100644
--- a/src/main/java/org/traccar/protocol/DolphinProtocol.java
+++ b/src/main/java/org/traccar/protocol/DolphinProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.config.Config;
import java.nio.ByteOrder;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class DolphinProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/DraginoProtocol.java b/src/main/java/org/traccar/protocol/DraginoProtocol.java
new file mode 100644
index 000000000..d33efe2ad
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/DraginoProtocol.java
@@ -0,0 +1,42 @@
+/*
+ * 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 jakarta.inject.Inject;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+public class DraginoProtocol extends BaseProtocol {
+
+ @Inject
+ public DraginoProtocol(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 DraginoProtocolDecoder(DraginoProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/DraginoProtocolDecoder.java b/src/main/java/org/traccar/protocol/DraginoProtocolDecoder.java
new file mode 100644
index 000000000..5f576d723
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/DraginoProtocolDecoder.java
@@ -0,0 +1,78 @@
+/*
+ * 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 jakarta.json.Json;
+import jakarta.json.JsonObject;
+import org.traccar.BaseHttpProtocolDecoder;
+import org.traccar.Protocol;
+import org.traccar.helper.DateUtil;
+import org.traccar.model.Position;
+import org.traccar.session.DeviceSession;
+
+import java.io.StringReader;
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+
+public class DraginoProtocolDecoder extends BaseHttpProtocolDecoder {
+
+ public DraginoProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ FullHttpRequest request = (FullHttpRequest) msg;
+ String content = request.content().toString(StandardCharsets.UTF_8);
+ JsonObject json = Json.createReader(new StringReader(content)).readObject();
+
+ String deviceId = json.getJsonObject("end_device_ids").getString("device_id");
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, deviceId);
+ if (deviceSession == null) {
+ sendResponse(channel, HttpResponseStatus.BAD_REQUEST);
+ return null;
+ }
+
+ JsonObject message = json.getJsonObject("uplink_message");
+ JsonObject decoded = message.getJsonObject("decoded_payload");
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setTime(DateUtil.parseDate(message.getString("received_at")));
+
+ position.setValid(true);
+ position.setLatitude(decoded.getJsonNumber("Latitude").doubleValue());
+ position.setLongitude(decoded.getJsonNumber("Longitude").doubleValue());
+
+ position.set("humidity", decoded.getJsonNumber("Hum").doubleValue());
+ position.set(Position.KEY_BATTERY, decoded.getJsonNumber("BatV").doubleValue());
+ position.set(Position.PREFIX_TEMP + 1, decoded.getJsonNumber("Tem").doubleValue());
+
+ if (Boolean.parseBoolean(decoded.getString("ALARM_status"))) {
+ position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ }
+
+ sendResponse(channel, HttpResponseStatus.OK);
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Dsf22Protocol.java b/src/main/java/org/traccar/protocol/Dsf22Protocol.java
index 06c99b0f9..ad349a7ff 100644
--- a/src/main/java/org/traccar/protocol/Dsf22Protocol.java
+++ b/src/main/java/org/traccar/protocol/Dsf22Protocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Dsf22Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/DualcamProtocol.java b/src/main/java/org/traccar/protocol/DualcamProtocol.java
index 363a2c5d9..4725cb180 100644
--- a/src/main/java/org/traccar/protocol/DualcamProtocol.java
+++ b/src/main/java/org/traccar/protocol/DualcamProtocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class DualcamProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/DualcamProtocolDecoder.java b/src/main/java/org/traccar/protocol/DualcamProtocolDecoder.java
index d03f7648d..411e2b9d7 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 - 2022 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.
@@ -42,13 +42,24 @@ public class DualcamProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_COMPLETE = 5;
public static final int MSG_FILE_REQUEST = 8;
public static final int MSG_INIT_REQUEST = 9;
+ public static final int MSG_PATH_REQUEST = 0x000C;
+ public static final int MSG_PATH_RESPONSE = 0x000D;
+ private String model;
private String uniqueId;
- private int packetCount;
- private int currentPacket;
+ private int dataSize;
+ private int dataCurrent;
private boolean video;
private ByteBuf media;
+ private boolean isPacketData() {
+ if (model == null) {
+ return dataSize < 8192;
+ } else {
+ return !"DSM".equals(model);
+ }
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -57,15 +68,21 @@ public class DualcamProtocolDecoder extends BaseProtocolDecoder {
int type = buf.readUnsignedShort();
+ DeviceSession deviceSession;
switch (type) {
case MSG_INIT:
buf.readUnsignedShort(); // protocol id
uniqueId = String.valueOf(buf.readLong());
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, uniqueId);
+ deviceSession = getDeviceSession(channel, remoteAddress, uniqueId);
long settings = buf.readUnsignedInt();
if (channel != null && deviceSession != null) {
+ model = getDeviceModel(deviceSession);
ByteBuf response = Unpooled.buffer();
- if (BitUtil.between(settings, 26, 30) > 0) {
+ if (BitUtil.check(settings, 25)) {
+ response.writeShort(MSG_PATH_REQUEST);
+ response.writeShort(2);
+ response.writeShort(0);
+ } else if (BitUtil.between(settings, 26, 30) > 0) {
response.writeShort(MSG_FILE_REQUEST);
String file;
if (BitUtil.check(settings, 26)) {
@@ -91,21 +108,29 @@ public class DualcamProtocolDecoder extends BaseProtocolDecoder {
break;
case MSG_START:
buf.readUnsignedShort(); // length
- packetCount = buf.readInt();
- currentPacket = 1;
+ dataSize = buf.readInt();
+ dataCurrent = isPacketData() ? 1 : 0;
media = Unpooled.buffer();
if (channel != null) {
ByteBuf response = Unpooled.buffer();
response.writeShort(MSG_RESUME);
response.writeShort(4);
- response.writeInt(currentPacket);
+ response.writeInt(dataCurrent);
channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
break;
case MSG_DATA:
- buf.readUnsignedShort(); // length
- media.writeBytes(buf, buf.readableBytes() - 2);
- if (currentPacket == packetCount) {
+ int length = buf.readUnsignedShort() - 2;
+ media.writeBytes(buf, length);
+ boolean finished;
+ if (isPacketData()) {
+ finished = dataCurrent == dataSize;
+ dataCurrent += 1;
+ } else {
+ finished = dataCurrent + length == dataSize;
+ dataCurrent += length;
+ }
+ if (finished) {
deviceSession = getDeviceSession(channel, remoteAddress);
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
@@ -126,8 +151,16 @@ public class DualcamProtocolDecoder extends BaseProtocolDecoder {
channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
return position;
- } else {
- currentPacket += 1;
+ }
+ break;
+ case MSG_PATH_RESPONSE:
+ String file = buf.readCharSequence(buf.readUnsignedShort(), StandardCharsets.US_ASCII).toString();
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeShort(MSG_FILE_REQUEST);
+ response.writeShort(file.length());
+ response.writeCharSequence(file, StandardCharsets.US_ASCII);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
break;
default:
diff --git a/src/main/java/org/traccar/protocol/DwayProtocol.java b/src/main/java/org/traccar/protocol/DwayProtocol.java
index 1096c945c..2ba1cf5f1 100644
--- a/src/main/java/org/traccar/protocol/DwayProtocol.java
+++ b/src/main/java/org/traccar/protocol/DwayProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class DwayProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/EasyTrackProtocol.java b/src/main/java/org/traccar/protocol/EasyTrackProtocol.java
index 39aa61580..25d4ef9a0 100644
--- a/src/main/java/org/traccar/protocol/EasyTrackProtocol.java
+++ b/src/main/java/org/traccar/protocol/EasyTrackProtocol.java
@@ -24,7 +24,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class EasyTrackProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java
index 805cf1197..b10ff4c64 100644
--- a/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.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.
@@ -79,26 +79,45 @@ public class EasyTrackProtocolDecoder extends BaseProtocolDecoder {
.any()
.compile();
+ private static final Pattern PATTERN_OBD = new PatternBuilder()
+ .text("*").expression("..,") // manufacturer
+ .number("(d+),") // imei
+ .text("OB,") // command
+ .text("BD$")
+ .number("V(d+.d);") // battery
+ .number("R(d+);") // rpm
+ .number("S(d+);") // speed
+ .number("P(d+.d);") // throttle
+ .number("O(d+.d);") // engine load
+ .number("C(d+);") // coolant temperature
+ .number("L(d+.d);") // fuel level
+ .number("[XY][MH]d+.d+;")
+ .number("M(d+);") // mileage
+ .number("F(d+.d+);") // fuel consumption
+ .number("T(d+);") // engine time
+ .any()
+ .compile();
+
private String decodeAlarm(long status) {
- if ((status & 0x02000000) != 0) {
+ if ((status & 0x02000000L) != 0) {
return Position.ALARM_GEOFENCE_ENTER;
}
- if ((status & 0x04000000) != 0) {
+ if ((status & 0x04000000L) != 0) {
return Position.ALARM_GEOFENCE_EXIT;
}
- if ((status & 0x08000000) != 0) {
+ if ((status & 0x08000000L) != 0) {
return Position.ALARM_LOW_BATTERY;
}
- if ((status & 0x20000000) != 0) {
+ if ((status & 0x20000000L) != 0) {
return Position.ALARM_VIBRATION;
}
- if ((status & 0x80000000) != 0) {
+ if ((status & 0x80000000L) != 0) {
return Position.ALARM_OVERSPEED;
}
- if ((status & 0x00010000) != 0) {
+ if ((status & 0x00010000L) != 0) {
return Position.ALARM_SOS;
}
- if ((status & 0x00040000) != 0) {
+ if ((status & 0x00040000L) != 0) {
return Position.ALARM_POWER_CUT;
}
return null;
@@ -115,7 +134,9 @@ public class EasyTrackProtocolDecoder extends BaseProtocolDecoder {
channel.writeAndFlush(new NetworkMessage(sentence + "#", remoteAddress));
}
- if (type.equals("JZ")) {
+ if (type.equals("OB")) {
+ return decodeObd(channel, remoteAddress, sentence);
+ } else if (type.equals("JZ")) {
return decodeCell(channel, remoteAddress, sentence);
} else {
return decodeLocation(channel, remoteAddress, sentence);
@@ -219,4 +240,35 @@ public class EasyTrackProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private Position decodeObd(Channel channel, SocketAddress remoteAddress, String sentence) {
+
+ Parser parser = new Parser(PATTERN_OBD, 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_BATTERY, parser.nextDouble());
+ position.set(Position.KEY_RPM, parser.nextInt());
+ position.set(Position.KEY_OBD_SPEED, parser.nextInt());
+ position.set(Position.KEY_THROTTLE, parser.nextDouble());
+ position.set(Position.KEY_ENGINE_LOAD, parser.nextDouble());
+ position.set(Position.KEY_COOLANT_TEMP, parser.nextInt());
+ position.set(Position.KEY_FUEL_LEVEL, parser.nextDouble());
+ position.set(Position.KEY_ODOMETER, parser.nextInt());
+ position.set(Position.KEY_FUEL_CONSUMPTION, parser.nextDouble());
+ position.set(Position.KEY_HOURS, parser.nextInt());
+
+ return position;
+ }
+
}
diff --git a/src/main/java/org/traccar/protocol/EelinkProtocol.java b/src/main/java/org/traccar/protocol/EelinkProtocol.java
index 35fd4fe65..2a3c0bd15 100644
--- a/src/main/java/org/traccar/protocol/EelinkProtocol.java
+++ b/src/main/java/org/traccar/protocol/EelinkProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class EelinkProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java b/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java
index cb0e10042..db1b365c3 100644
--- a/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java
@@ -314,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 f257271d4..5450d9f01 100644
--- a/src/main/java/org/traccar/protocol/EgtsProtocol.java
+++ b/src/main/java/org/traccar/protocol/EgtsProtocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class EgtsProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/EnforaProtocol.java b/src/main/java/org/traccar/protocol/EnforaProtocol.java
index ebde56f70..3b796db25 100644
--- a/src/main/java/org/traccar/protocol/EnforaProtocol.java
+++ b/src/main/java/org/traccar/protocol/EnforaProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class EnforaProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/EnnfuProtocol.java b/src/main/java/org/traccar/protocol/EnnfuProtocol.java
index e326481fa..a3ff943fa 100644
--- a/src/main/java/org/traccar/protocol/EnnfuProtocol.java
+++ b/src/main/java/org/traccar/protocol/EnnfuProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class EnnfuProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/EnvotechProtocol.java b/src/main/java/org/traccar/protocol/EnvotechProtocol.java
index dffa1c991..e432ac07c 100644
--- a/src/main/java/org/traccar/protocol/EnvotechProtocol.java
+++ b/src/main/java/org/traccar/protocol/EnvotechProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class EnvotechProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/EsealProtocol.java b/src/main/java/org/traccar/protocol/EsealProtocol.java
index 0ed80dc6f..eae4cf2aa 100644
--- a/src/main/java/org/traccar/protocol/EsealProtocol.java
+++ b/src/main/java/org/traccar/protocol/EsealProtocol.java
@@ -24,7 +24,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class EsealProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/EskyProtocol.java b/src/main/java/org/traccar/protocol/EskyProtocol.java
index cb2f59dc8..002e268ba 100644
--- a/src/main/java/org/traccar/protocol/EskyProtocol.java
+++ b/src/main/java/org/traccar/protocol/EskyProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class EskyProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/ExtremTracProtocol.java b/src/main/java/org/traccar/protocol/ExtremTracProtocol.java
index ffc941b69..23a993fe4 100644
--- a/src/main/java/org/traccar/protocol/ExtremTracProtocol.java
+++ b/src/main/java/org/traccar/protocol/ExtremTracProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class ExtremTracProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/FifotrackProtocol.java b/src/main/java/org/traccar/protocol/FifotrackProtocol.java
index fd2beaabb..e98ad84cc 100644
--- a/src/main/java/org/traccar/protocol/FifotrackProtocol.java
+++ b/src/main/java/org/traccar/protocol/FifotrackProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class FifotrackProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
index a9d77b46e..59019830f 100644
--- a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
@@ -64,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
@@ -183,6 +183,8 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
case 30:
case 32:
return Position.ALARM_JAMMING;
+ case 31:
+ return Position.ALARM_FALL_DOWN;
case 33:
return Position.ALARM_GEOFENCE_EXIT;
case 34:
@@ -192,6 +194,10 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
case 40:
case 41:
return Position.ALARM_TEMPERATURE;
+ case 53:
+ return Position.ALARM_POWER_ON;
+ case 54:
+ return Position.ALARM_POWER_OFF;
default:
return null;
}
@@ -235,13 +241,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(":");
@@ -290,6 +298,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));
@@ -308,11 +317,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);
}
}
diff --git a/src/main/java/org/traccar/protocol/FleetGuideProtocol.java b/src/main/java/org/traccar/protocol/FleetGuideProtocol.java
new file mode 100644
index 000000000..46611c25c
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/FleetGuideProtocol.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2024 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import jakarta.inject.Inject;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+public class FleetGuideProtocol extends BaseProtocol {
+
+ @Inject
+ public FleetGuideProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), true) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
+ pipeline.addLast(new FleetGuideProtocolDecoder(FleetGuideProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/FleetGuideProtocolDecoder.java b/src/main/java/org/traccar/protocol/FleetGuideProtocolDecoder.java
new file mode 100644
index 000000000..8f679525b
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/FleetGuideProtocolDecoder.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright 2024 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.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;
+import java.util.LinkedList;
+import java.util.List;
+
+public class FleetGuideProtocolDecoder extends BaseProtocolDecoder {
+
+ public FleetGuideProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_EMPTY = 0;
+ public static final int MSG_SYNC_REQ = 1;
+ public static final int MSG_SYNC_ACK = 2;
+ public static final int MSG_DATA_R_ACK = 3;
+ public static final int MSG_DATA_N_ACK = 4;
+ public static final int MSG_REP_R_ACK = 5;
+ public static final int MSG_REP_N_ACK = 6;
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.readUnsignedByte(); // signature
+ int options = buf.readUnsignedShortLE();
+ int length = BitUtil.to(options, 11);
+
+ DeviceSession deviceSession;
+ Long deviceId;
+ if (BitUtil.check(options, 11)) {
+ deviceId = buf.readUnsignedIntLE();
+ deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(deviceId));
+ } else {
+ deviceId = null;
+ deviceSession = getDeviceSession(channel, remoteAddress);
+ }
+ if (deviceSession == null) {
+ return null;
+ }
+
+ int type;
+ Integer index;
+ if (BitUtil.check(options, 12)) {
+ int value = buf.readUnsignedByte();
+ type = BitUtil.to(value, 4);
+ index = BitUtil.from(value, 4);
+ } else {
+ type = 0;
+ index = null;
+ }
+
+ if (type != MSG_DATA_N_ACK && type != MSG_REP_N_ACK) {
+ Integer responseType;
+ if (type == MSG_SYNC_REQ) {
+ responseType = MSG_SYNC_ACK;
+ } else {
+ responseType = null;
+ }
+ sendResponse(channel, remoteAddress, deviceId, responseType, index);
+ }
+
+ if (BitUtil.check(options, 13)) {
+ buf.readUnsignedShortLE(); // acknowledgement
+ }
+
+ ByteBuf data;
+ if (BitUtil.check(options, 14)) {
+ data = decompress(buf.readSlice(length));
+ } else {
+ data = buf.readRetainedSlice(length);
+ }
+
+ List<Position> positions = new LinkedList<>();
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ while (data.isReadable()) {
+
+ int recordHeader = data.readUnsignedShortLE();
+ int recordLength = BitUtil.to(recordHeader, 10);
+ int recordType = BitUtil.from(recordHeader, 10);
+ int recordEndIndex = data.readerIndex() + recordLength;
+
+ if (recordType == 0 && position.getDeviceTime() != null) {
+ processPosition(positions, position);
+ position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ }
+
+ switch (recordType) {
+ case 0:
+ position.setTime(new Date((data.readUnsignedIntLE() + 1262304000) * 1000)); // since 2010-01-01
+ break;
+ case 1:
+ position.setLatitude(data.readUnsignedIntLE() * 90.0 / 0xFFFFFFFFL);
+ position.setLongitude(data.readUnsignedIntLE() * 180.0 / 0xFFFFFFFFL);
+ int speed = data.readUnsignedShortLE();
+ position.setSpeed(UnitsConverter.knotsFromKph(BitUtil.to(speed, 14) * 0.1));
+ if (BitUtil.check(speed, 14)) {
+ position.setLatitude(-position.getLatitude());
+ }
+ if (BitUtil.check(speed, 15)) {
+ position.setLongitude(-position.getLongitude());
+ }
+ int course = data.readUnsignedShortLE();
+ position.setSpeed(BitUtil.to(course, 9));
+ int motion = BitUtil.between(course, 9, 11);
+ if (motion > 0) {
+ position.set(Position.KEY_MOTION, motion == 1);
+ }
+ position.set(Position.KEY_SATELLITES, BitUtil.from(course, 11));
+ int altitude = data.readUnsignedShortLE();
+ position.setAltitude(BitUtil.to(altitude, 14));
+ if (BitUtil.check(altitude, 14)) {
+ position.setAltitude(-position.getAltitude());
+ }
+ break;
+ case 3:
+ int powerLow = data.readUnsignedByte();
+ int powerFlags = data.readUnsignedByte();
+ int batteryHigh = data.readUnsignedByte();
+ position.set(Position.KEY_POWER, (powerLow + (BitUtil.to(powerFlags, 5) << 8)) * 0.01);
+ position.set(Position.KEY_IGNITION, BitUtil.check(powerFlags, 5));
+ position.set(Position.KEY_BATTERY, (BitUtil.from(powerFlags, 6) + (batteryHigh << 2)) * 0.01);
+ if (recordLength >= 4) {
+ int extraFlags = data.readUnsignedByte();
+ if (BitUtil.check(extraFlags, 0)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_LOW_POWER);
+ }
+ if (BitUtil.check(extraFlags, 1)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY);
+ }
+ }
+ break;
+ case 6:
+ position.set(Position.KEY_INPUT, data.readUnsignedByte());
+ break;
+ case 7:
+ position.set(Position.KEY_OUTPUT, data.readUnsignedByte());
+ break;
+ case 8:
+ int adcMask = data.readUnsignedByte();
+ for (int i = 0; i < 8; i++) {
+ if (BitUtil.check(adcMask, i)) {
+ position.set(Position.PREFIX_ADC + (i + 1), data.readUnsignedShortLE());
+ }
+ }
+ break;
+ case 11:
+ int fuelMask = data.readUnsignedByte();
+ for (int i = 1; i < 8; i++) {
+ if (BitUtil.check(fuelMask, i)) {
+ position.set("fuel" + i, data.readUnsignedShortLE());
+ }
+ }
+ break;
+ case 12:
+ int fuelTempMask = data.readUnsignedByte();
+ for (int i = 1; i < 8; i++) {
+ if (BitUtil.check(fuelTempMask, i)) {
+ position.set("fuelTemp" + i, (int) data.readByte());
+ }
+ }
+ break;
+ case 13:
+ int tempMask = data.readUnsignedByte();
+ for (int i = 0; i < 8; i++) {
+ if (BitUtil.check(tempMask, i)) {
+ position.set(Position.PREFIX_TEMP + (i + 1), data.readShortLE() * 0.01);
+ }
+ }
+ break;
+ case 18:
+ int sensorIndex = data.readUnsignedByte();
+ switch (recordLength - 1) {
+ case 1:
+ position.set("sensor" + sensorIndex, data.readUnsignedByte());
+ break;
+ case 2:
+ position.set("sensor" + sensorIndex, data.readUnsignedShortLE());
+ break;
+ case 4:
+ position.set("sensor" + sensorIndex, data.readUnsignedIntLE());
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ data.readerIndex(recordEndIndex);
+
+ }
+
+ processPosition(positions, position);
+
+ data.release();
+
+ return positions.isEmpty() ? null : positions;
+ }
+
+ private void processPosition(List<Position> positions, Position position) {
+ if (!position.getAttributes().isEmpty()) {
+ if (position.getFixTime() == null) {
+ position.setTime(new Date());
+ }
+ if (!position.getAttributes().containsKey(Position.KEY_SATELLITES)) {
+ getLastLocation(position, null);
+ }
+ positions.add(position);
+ }
+ }
+
+
+ private void sendResponse(
+ Channel channel, SocketAddress remoteAddress, Long deviceId, Integer type, Integer index) {
+ if (channel != null) {
+
+ ByteBuf response = Unpooled.buffer();
+ response.writeByte(0x53); // signature
+
+ int options = 0;
+ if (deviceId != null) {
+ options |= 1 << 11;
+ }
+ if (type != null) {
+ options |= 1 << 12;
+ }
+ if (index != null) {
+ options |= 1 << 13;
+ }
+ response.writeShortLE(options);
+
+ if (deviceId != null) {
+ response.writeIntLE(deviceId.intValue());
+ }
+ if (type != null) {
+ response.writeByte(type);
+ }
+ if (index != null) {
+ int mask = (1 << (index + 1)) - 1;
+ response.writeShortLE(mask);
+ }
+ response.writeShortLE(Checksum.crc16(
+ Checksum.CRC16_CCITT_FALSE, response.nioBuffer(1, response.writerIndex() - 1)));
+
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ }
+
+ private int readVarSize(ByteBuf buf) {
+ int b;
+ int y = 0;
+ do {
+ b = buf.readUnsignedByte();
+ y = (y << 7) | (b & 0x0000007f);
+ } while ((b & 0x00000080) > 0);
+
+ return y;
+ }
+
+ private ByteBuf decompress(ByteBuf in) {
+
+ ByteBuf out = Unpooled.buffer();
+
+ if (in.readableBytes() < 1) {
+ return out;
+ }
+
+ int marker = in.readUnsignedByte();
+
+ do {
+ int symbol = in.readUnsignedByte();
+ if (symbol == marker) {
+ if (in.getUnsignedByte(in.readerIndex()) == 0) {
+ out.writeByte(marker);
+ in.skipBytes(1);
+ } else {
+ int length = readVarSize(in);
+ int offset = readVarSize(in);
+
+ for (int i = 0; i < length; i++) {
+ out.writeByte(out.getUnsignedByte(out.writerIndex() - offset));
+ }
+ }
+ } else {
+ out.writeByte(symbol);
+ }
+ } while (in.isReadable());
+
+ return out;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/FlespiProtocol.java b/src/main/java/org/traccar/protocol/FlespiProtocol.java
index 374cf77e2..0d8448845 100644
--- a/src/main/java/org/traccar/protocol/FlespiProtocol.java
+++ b/src/main/java/org/traccar/protocol/FlespiProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class FlespiProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/FlespiProtocolDecoder.java b/src/main/java/org/traccar/protocol/FlespiProtocolDecoder.java
index 6e6f9c700..ea076afd8 100644
--- a/src/main/java/org/traccar/protocol/FlespiProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FlespiProtocolDecoder.java
@@ -24,12 +24,12 @@ 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;
@@ -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());
diff --git a/src/main/java/org/traccar/protocol/FlexApiProtocol.java b/src/main/java/org/traccar/protocol/FlexApiProtocol.java
index 088072d2d..b2e3f5d00 100644
--- a/src/main/java/org/traccar/protocol/FlexApiProtocol.java
+++ b/src/main/java/org/traccar/protocol/FlexApiProtocol.java
@@ -24,7 +24,7 @@ import org.traccar.config.Config;
import java.nio.charset.StandardCharsets;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class FlexApiProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/FlexApiProtocolDecoder.java b/src/main/java/org/traccar/protocol/FlexApiProtocolDecoder.java
index 2dec44e64..fb76673ca 100644
--- a/src/main/java/org/traccar/protocol/FlexApiProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FlexApiProtocolDecoder.java
@@ -23,8 +23,8 @@ 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 5397156cb..293b9b12b 100644
--- a/src/main/java/org/traccar/protocol/FlexCommProtocol.java
+++ b/src/main/java/org/traccar/protocol/FlexCommProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class FlexCommProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/FlexibleReportProtocol.java b/src/main/java/org/traccar/protocol/FlexibleReportProtocol.java
index 61e315af9..a16e61458 100644
--- a/src/main/java/org/traccar/protocol/FlexibleReportProtocol.java
+++ b/src/main/java/org/traccar/protocol/FlexibleReportProtocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class FlexibleReportProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/FlextrackProtocol.java b/src/main/java/org/traccar/protocol/FlextrackProtocol.java
index ebac8b4de..b6353de9d 100644
--- a/src/main/java/org/traccar/protocol/FlextrackProtocol.java
+++ b/src/main/java/org/traccar/protocol/FlextrackProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class FlextrackProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/FoxProtocol.java b/src/main/java/org/traccar/protocol/FoxProtocol.java
index fa45b3817..edb496f11 100644
--- a/src/main/java/org/traccar/protocol/FoxProtocol.java
+++ b/src/main/java/org/traccar/protocol/FoxProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class FoxProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/FreedomProtocol.java b/src/main/java/org/traccar/protocol/FreedomProtocol.java
index dac117c04..87404094a 100644
--- a/src/main/java/org/traccar/protocol/FreedomProtocol.java
+++ b/src/main/java/org/traccar/protocol/FreedomProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class FreedomProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/FreematicsProtocol.java b/src/main/java/org/traccar/protocol/FreematicsProtocol.java
index dce4994ab..c4076f330 100644
--- a/src/main/java/org/traccar/protocol/FreematicsProtocol.java
+++ b/src/main/java/org/traccar/protocol/FreematicsProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class FreematicsProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/FreematicsProtocolDecoder.java b/src/main/java/org/traccar/protocol/FreematicsProtocolDecoder.java
index 4e5200f37..d0402cc94 100644
--- a/src/main/java/org/traccar/protocol/FreematicsProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FreematicsProtocolDecoder.java
@@ -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));
@@ -165,7 +165,7 @@ public class FreematicsProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_RPM, Integer.parseInt(value));
break;
case 0x10d:
- position.set(Position.KEY_OBD_SPEED, UnitsConverter.knotsFromKph(Integer.parseInt(value)));
+ position.set(Position.KEY_OBD_SPEED, Integer.parseInt(value));
break;
case 0x111:
position.set(Position.KEY_THROTTLE, Integer.parseInt(value));
diff --git a/src/main/java/org/traccar/protocol/FutureWayProtocol.java b/src/main/java/org/traccar/protocol/FutureWayProtocol.java
index 715dd3c9c..20586bede 100644
--- a/src/main/java/org/traccar/protocol/FutureWayProtocol.java
+++ b/src/main/java/org/traccar/protocol/FutureWayProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class FutureWayProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/G1rusProtocol.java b/src/main/java/org/traccar/protocol/G1rusProtocol.java
index f1823762d..b3904b357 100644
--- a/src/main/java/org/traccar/protocol/G1rusProtocol.java
+++ b/src/main/java/org/traccar/protocol/G1rusProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class G1rusProtocol extends BaseProtocol {
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 90e95574a..95ce4edde 100644
--- a/src/main/java/org/traccar/protocol/GalileoProtocol.java
+++ b/src/main/java/org/traccar/protocol/GalileoProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class GalileoProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/GatorProtocol.java b/src/main/java/org/traccar/protocol/GatorProtocol.java
index 7341b69a3..d29ed9ce7 100644
--- a/src/main/java/org/traccar/protocol/GatorProtocol.java
+++ b/src/main/java/org/traccar/protocol/GatorProtocol.java
@@ -20,17 +20,25 @@ import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
+import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class GatorProtocol extends BaseProtocol {
@Inject
public GatorProtocol(Config config) {
+ setSupportedDataCommands(
+ Command.TYPE_POSITION_SINGLE,
+ Command.TYPE_ENGINE_RESUME,
+ Command.TYPE_ENGINE_STOP,
+ Command.TYPE_SET_SPEED_LIMIT,
+ Command.TYPE_SET_ODOMETER);
addServer(new TrackerServer(config, getName(), false) {
@Override
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));
}
});
diff --git a/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java b/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java
index 644caee81..a9c620090 100644
--- a/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java
@@ -37,6 +37,11 @@ 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_OVERSPEED_ALARM = 0x3F;
+ public static final int MSG_RESET_MILEAGE = 0x6B;
+ public static final int MSG_RESTORE_OIL_DUCT = 0x38;
+ public static final int MSG_CLOSE_OIL_DUCT = 0x39;
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..6c6b9a54a
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/GatorProtocolEncoder.java
@@ -0,0 +1,93 @@
+/*
+ * 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 content) {
+ ByteBuf buf = Unpooled.buffer();
+
+ buf.writeByte(0x24);
+ buf.writeByte(0x24);
+ buf.writeByte(type);
+ buf.writeByte(0x00);
+
+ buf.writeByte(4 + 1 + (content != null ? content.readableBytes() : 0) + 1); // length
+
+ ByteBuf pseudoIPAddress = encodeId(deviceId);
+ buf.writeBytes(pseudoIPAddress);
+
+ if (content != null) {
+ buf.writeBytes(content);
+ }
+
+ int checksum = Checksum.xor(buf.nioBuffer());
+ buf.writeByte(checksum);
+
+ buf.writeByte(0x0D);
+
+ return buf;
+ }
+
+ @Override
+ protected Object encodeCommand(Command command) {
+
+ ByteBuf content = Unpooled.buffer();
+
+ switch (command.getType()) {
+ case Command.TYPE_POSITION_SINGLE:
+ return encodeContent(command.getDeviceId(), GatorProtocolDecoder.MSG_POSITION_REQUEST, null);
+ case Command.TYPE_ENGINE_STOP:
+ return encodeContent(command.getDeviceId(), GatorProtocolDecoder.MSG_CLOSE_OIL_DUCT, null);
+ case Command.TYPE_ENGINE_RESUME:
+ return encodeContent(command.getDeviceId(), GatorProtocolDecoder.MSG_RESTORE_OIL_DUCT, null);
+ case Command.TYPE_SET_SPEED_LIMIT:
+ content.writeByte(command.getInteger(Command.KEY_DATA));
+ return encodeContent(command.getDeviceId(), GatorProtocolDecoder.MSG_RESET_MILEAGE, content);
+ case Command.TYPE_SET_ODOMETER:
+ content.writeShort(command.getInteger(Command.KEY_DATA));
+ return encodeContent(command.getDeviceId(), GatorProtocolDecoder.MSG_OVERSPEED_ALARM, content);
+ default:
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/org/traccar/protocol/GenxProtocol.java b/src/main/java/org/traccar/protocol/GenxProtocol.java
index 97d8633a0..7e5a6a69e 100644
--- a/src/main/java/org/traccar/protocol/GenxProtocol.java
+++ b/src/main/java/org/traccar/protocol/GenxProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class GenxProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Gl100Protocol.java b/src/main/java/org/traccar/protocol/Gl100Protocol.java
index e1748c9a0..fdd79648f 100644
--- a/src/main/java/org/traccar/protocol/Gl100Protocol.java
+++ b/src/main/java/org/traccar/protocol/Gl100Protocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Gl100Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Gl200Protocol.java b/src/main/java/org/traccar/protocol/Gl200Protocol.java
index c7b6a8e7c..e3ddbb46e 100644
--- a/src/main/java/org/traccar/protocol/Gl200Protocol.java
+++ b/src/main/java/org/traccar/protocol/Gl200Protocol.java
@@ -22,7 +22,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Gl200Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Gl200ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200ProtocolDecoder.java
index a9736c9e7..9b4e05c35 100644
--- a/src/main/java/org/traccar/protocol/Gl200ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gl200ProtocolDecoder.java
@@ -22,7 +22,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import org.traccar.Protocol;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.net.SocketAddress;
public class Gl200ProtocolDecoder extends BaseProtocolDecoder {
diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
index 517499f02..2e5ffa8d6 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 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2024 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,12 +15,15 @@
*/
package org.traccar.protocol;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.config.Keys;
import org.traccar.helper.BitUtil;
+import org.traccar.helper.DataConverter;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -28,15 +31,14 @@ import org.traccar.model.CellTower;
import org.traccar.model.Network;
import org.traccar.model.Position;
import org.traccar.model.WifiAccessPoint;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.channel.Channel;
+import org.traccar.session.DeviceSession;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
+import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.TimeZone;
@@ -47,8 +49,12 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
private boolean ignoreFixTime;
+ private final DateFormat dateFormat;
+
public Gl200TextProtocolDecoder(Protocol protocol) {
super(protocol);
+ dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
}
@Override
@@ -56,21 +62,67 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
ignoreFixTime = getConfig().getBoolean(Keys.PROTOCOL_IGNORE_FIX_TIME.withPrefix(getProtocolName()));
}
- private static final Pattern PATTERN_ACK = new PatternBuilder()
- .text("+ACK:GT")
- .expression("...,") // type
- .number("([0-9A-Z]{2}xxxx),") // protocol version
- .number("(d{15}|x{14}),") // imei
- .any().text(",")
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd),") // time (hhmmss)
- .number("(xxxx)") // counter
- .text("$").optional()
- .compile();
+ private String getDeviceModel(DeviceSession deviceSession, String value) {
+ String model = value.isEmpty() ? getDeviceModel(deviceSession) : value;
+ return model != null ? model.toUpperCase() : "";
+ }
+
+ private Position initPosition(Parser parser, Channel channel, SocketAddress remoteAddress) {
+ if (parser.matches()) {
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession != null) {
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ return position;
+ }
+ }
+ return null;
+ }
+
+ private void decodeDeviceTime(Position position, Parser parser) {
+ if (parser.hasNext(6)) {
+ if (ignoreFixTime) {
+ position.setTime(parser.nextDateTime());
+ } else {
+ position.setDeviceTime(parser.nextDateTime());
+ }
+ }
+ }
+
+ private Long parseHours(String hoursString) {
+ if (hoursString != null && !hoursString.isEmpty()) {
+ String[] hours = hoursString.split(":");
+ 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 Position decodeAck(Channel channel, SocketAddress remoteAddress, String[] values) throws ParseException {
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[2]);
+ if (deviceSession == null) {
+ return null;
+ }
+ if (values[0].equals("+ACK:GTHBD")) {
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage(
+ "+SACK:GTHBD," + values[1] + "," + values[values.length - 1] + "$", remoteAddress));
+ }
+ } else {
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ getLastLocation(position, dateFormat.parse(values[values.length - 2]));
+ position.setValid(false);
+ position.set(Position.KEY_RESULT, values[0]);
+ return position;
+ }
+ return null;
+ }
private static final Pattern PATTERN_INF = new PatternBuilder()
.text("+").expression("(?:RESP|BUFF):GTINF,")
- .number("[0-9A-Z]{2}xxxx,") // protocol version
+ .expression("(?:.{6}|.{10})?,") // protocol version
.number("(d{15}|x{14}),") // imei
.expression("(?:[0-9A-Z]{17},)?") // vin
.expression("(?:[^,]+)?,") // device name
@@ -109,374 +161,6 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
.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_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 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 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()) {
- String protocolVersion = parser.next();
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
- if (deviceSession == null) {
- return null;
- }
- if (type.equals("HBD")) {
- if (channel != null) {
- parser.skip(6);
- channel.writeAndFlush(new NetworkMessage(
- "+SACK:GTHBD," + protocolVersion + "," + parser.next() + "$", remoteAddress));
- }
- } else {
- Position position = new Position(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
- getLastLocation(position, parser.nextDateTime());
- position.setValid(false);
- position.set(Position.KEY_RESULT, "Command " + type + " accepted");
- return position;
- }
- }
- return null;
- }
-
- private Position initPosition(Parser parser, Channel channel, SocketAddress remoteAddress) {
- if (parser.matches()) {
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
- if (deviceSession != null) {
- Position position = new Position(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
- return position;
- }
- }
- return null;
- }
-
- private void decodeDeviceTime(Position position, Parser parser) {
- if (parser.hasNext(6)) {
- if (ignoreFixTime) {
- position.setTime(parser.nextDateTime());
- } else {
- position.setDeviceTime(parser.nextDateTime());
- }
- }
- }
-
- 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)
- + (hours.length > 2 ? Integer.parseInt(hours[2]) : 0)) * 1000;
- }
- return null;
- }
-
private Object decodeInf(Channel channel, SocketAddress remoteAddress, String sentence) {
Parser parser = new Parser(PATTERN_INF, sentence);
Position position = initPosition(parser, channel, remoteAddress);
@@ -537,6 +221,20 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private static final Pattern PATTERN_VER = new PatternBuilder()
+ .text("+").expression("(?:RESP|BUFF):GTVER,")
+ .expression("(?:.{6}|.{10})?,") // 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);
@@ -554,9 +252,35 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
}
private void skipLocation(Parser parser) {
- parser.skip(19);
+ parser.skip(20);
}
+ 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)
+ .groupBegin()
+ .number(",d+") // wifi count
+ .number("((?:,x{12},-d+,,,)+)") // wifi
+ .groupEnd("?")
+ .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);
@@ -575,22 +299,125 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
getLastLocation(position, null);
}
+ Network network = new Network();
+
+ if (parser.hasNext()) {
+ String[] values = parser.next().split(",");
+ for (int i = 0; i < values.length; i += 5) {
+ String mac = values[i + 1].replaceAll("(..)", "$1:");
+ network.addWifiAccessPoint(WifiAccessPoint.from(
+ mac.substring(0, mac.length() - 1), Integer.parseInt(values[i + 2])));
+ }
+ }
+
if (parser.hasNext(6)) {
int mcc = parser.nextInt();
int mnc = parser.nextInt();
if (parser.hasNext(2)) {
- position.setNetwork(new Network(CellTower.from(mcc, mnc, parser.nextInt(), parser.nextInt())));
+ network.addCellTower(CellTower.from(mcc, mnc, parser.nextInt(), parser.nextInt()));
}
if (parser.hasNext(2)) {
- position.setNetwork(new Network(CellTower.from(mcc, mnc, parser.nextHexInt(), parser.nextHexInt())));
+ network.addCellTower(CellTower.from(mcc, mnc, parser.nextHexInt(), parser.nextHexInt()));
}
}
+ if (network.getWifiAccessPoints() != null || network.getCellTowers() != null) {
+ position.setNetwork(network);
+ }
+
if (parser.hasNext()) {
position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
}
}
+ private int decodeLocation(Position position, String model, String[] values, int index) throws ParseException {
+ double hdop = values[index++].isEmpty() ? 0 : Double.parseDouble(values[index - 1]);
+ position.set(Position.KEY_HDOP, hdop);
+
+ position.setSpeed(UnitsConverter.knotsFromKph(
+ values[index++].isEmpty() ? 0 : Double.parseDouble(values[index - 1])));
+ position.setCourse(values[index++].isEmpty() ? 0 : Integer.parseInt(values[index - 1]));
+ position.setAltitude(values[index++].isEmpty() ? 0 : Double.parseDouble(values[index - 1]));
+
+ if (!values[index].isEmpty()) {
+ position.setValid(true);
+ position.setLongitude(values[index++].isEmpty() ? 0 : Double.parseDouble(values[index - 1]));
+ position.setLatitude(values[index++].isEmpty() ? 0 : Double.parseDouble(values[index - 1]));
+ position.setTime(dateFormat.parse(values[index++]));
+ } else {
+ index += 3;
+ getLastLocation(position, null);
+ }
+
+ Network network = new Network();
+
+ if (!values[index].isEmpty()) {
+ network.addCellTower(CellTower.from(
+ Integer.parseInt(values[index++]),
+ Integer.parseInt(values[index++]),
+ Integer.parseInt(values[index++], 16),
+ Long.parseLong(values[index++], 16)));
+ } else {
+ index += 4;
+ }
+
+ if (network.getWifiAccessPoints() != null || network.getCellTowers() != null) {
+ position.setNetwork(network);
+ }
+
+ if (model.startsWith("GL5")) {
+ index += 1; // csq rssi
+ index += 1; // csq ber
+ }
+
+ if (!values[index++].isEmpty()) {
+ int appendMask = Integer.parseInt(values[index - 1]);
+ if (BitUtil.check(appendMask, 0)) {
+ position.set(Position.KEY_SATELLITES, Integer.parseInt(values[index++]));
+ }
+ if (BitUtil.check(appendMask, 1)) {
+ index += 1; // trigger type
+ }
+ }
+
+ return index;
+ }
+
+ private static final Pattern PATTERN_OBD = new PatternBuilder()
+ .text("+RESP:GTOBD,")
+ .expression("(?:.{6}|.{10})?,") // 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);
@@ -625,104 +452,140 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
return position;
}
- private Object decodeCan(Channel channel, SocketAddress remoteAddress, String sentence) throws ParseException {
- Position position = new Position(getProtocolName());
-
+ private Object decodeCan(Channel channel, SocketAddress remoteAddress, String[] v) throws ParseException {
int index = 0;
- String[] values = sentence.split(",");
-
index += 1; // header
index += 1; // protocol version
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, v[index++]);
+ if (deviceSession == null) {
+ return null;
+ }
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]);
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- index += 1; // device name
+ String model = getDeviceModel(deviceSession, v[index++]);
index += 1; // report type
- index += 1; // canbus state
- long reportMask = Long.parseLong(values[index++], 16);
+ index += 1; // can bus state
+ long reportMask = Long.parseLong(v[index++], 16);
long reportMaskExt = 0;
if (BitUtil.check(reportMask, 0)) {
- position.set(Position.KEY_VIN, values[index++]);
+ position.set(Position.KEY_VIN, v[index++]);
}
- if (BitUtil.check(reportMask, 1)) {
- position.set(Position.KEY_IGNITION, Integer.parseInt(values[index++]) > 0);
+ if (BitUtil.check(reportMask, 1) && !v[index++].isEmpty()) {
+ position.set(Position.KEY_IGNITION, Integer.parseInt(v[index - 1]) > 0);
}
- if (BitUtil.check(reportMask, 2)) {
- position.set(Position.KEY_OBD_ODOMETER, values[index++]);
+ if (BitUtil.check(reportMask, 2) && !v[index++].isEmpty()) {
+ position.set(Position.KEY_OBD_ODOMETER, Integer.parseInt(v[index - 1].substring(1)));
}
- if (BitUtil.check(reportMask, 3) && !values[index++].isEmpty()) {
- position.set(Position.KEY_FUEL_USED, Double.parseDouble(values[index - 1]));
+ if (BitUtil.check(reportMask, 3) && !v[index++].isEmpty()) {
+ position.set(Position.KEY_FUEL_USED, Double.parseDouble(v[index - 1]));
}
- if (BitUtil.check(reportMask, 5) && !values[index++].isEmpty()) {
- position.set(Position.KEY_RPM, Integer.parseInt(values[index - 1]));
+ if (BitUtil.check(reportMask, 5) && !v[index++].isEmpty()) {
+ position.set(Position.KEY_RPM, Integer.parseInt(v[index - 1]));
}
- if (BitUtil.check(reportMask, 4) && !values[index++].isEmpty()) {
- position.set(Position.KEY_OBD_SPEED, UnitsConverter.knotsFromKph(Integer.parseInt(values[index - 1])));
+ if (BitUtil.check(reportMask, 4) && !v[index++].isEmpty()) {
+ position.set(Position.KEY_OBD_SPEED, Integer.parseInt(v[index - 1]));
}
- if (BitUtil.check(reportMask, 6) && !values[index++].isEmpty()) {
- position.set(Position.KEY_COOLANT_TEMP, Integer.parseInt(values[index - 1]));
+ if (BitUtil.check(reportMask, 6) && !v[index++].isEmpty()) {
+ position.set(Position.KEY_COOLANT_TEMP, Integer.parseInt(v[index - 1]));
}
- if (BitUtil.check(reportMask, 7) && !values[index++].isEmpty()) {
- position.set(Position.KEY_FUEL_CONSUMPTION, Double.parseDouble(values[index - 1].substring(1)));
+ if (BitUtil.check(reportMask, 7) && !v[index++].isEmpty()) {
+ String value = v[index - 1];
+ if (value.startsWith("L/H")) {
+ position.set(Position.KEY_FUEL_CONSUMPTION, Double.parseDouble(value.substring(3)));
+ }
}
- if (BitUtil.check(reportMask, 8) && !values[index++].isEmpty()) {
- position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(values[index - 1].substring(1)));
+ if (BitUtil.check(reportMask, 8) && !v[index++].isEmpty()) {
+ position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(v[index - 1].substring(1)));
}
- if (BitUtil.check(reportMask, 9) && !values[index++].isEmpty()) {
- position.set("range", Long.parseLong(values[index - 1]) * 100);
+ if (BitUtil.check(reportMask, 9) && !v[index++].isEmpty()) {
+ position.set("range", Long.parseLong(v[index - 1]) * 100);
}
- if (BitUtil.check(reportMask, 10) && !values[index++].isEmpty()) {
- position.set(Position.KEY_THROTTLE, Integer.parseInt(values[index - 1]));
+ if (BitUtil.check(reportMask, 10) && !v[index++].isEmpty()) {
+ position.set(Position.KEY_THROTTLE, Integer.parseInt(v[index - 1]));
}
- if (BitUtil.check(reportMask, 11) && !values[index++].isEmpty()) {
- position.set(Position.KEY_HOURS, UnitsConverter.msFromHours(Double.parseDouble(values[index - 1])));
+ if (BitUtil.check(reportMask, 11) && !v[index++].isEmpty()) {
+ position.set(Position.KEY_HOURS, UnitsConverter.msFromHours(Double.parseDouble(v[index - 1])));
}
- if (BitUtil.check(reportMask, 12)) {
- position.set("drivingHours", Double.parseDouble(values[index++]));
+ if (BitUtil.check(reportMask, 12) && !v[index++].isEmpty()) {
+ position.set(Position.KEY_DRIVING_TIME, Double.parseDouble(v[index - 1]));
}
- if (BitUtil.check(reportMask, 13)) {
- position.set("idleHours", Double.parseDouble(values[index++]));
+ if (BitUtil.check(reportMask, 13) && !v[index++].isEmpty()) {
+ position.set("idleHours", Double.parseDouble(v[index - 1]));
}
- if (BitUtil.check(reportMask, 14) && !values[index++].isEmpty()) {
- position.set("idleFuelConsumption", Double.parseDouble(values[index - 1]));
+ if (BitUtil.check(reportMask, 14) && !v[index++].isEmpty()) {
+ position.set("idleFuelConsumption", Double.parseDouble(v[index - 1]));
}
- if (BitUtil.check(reportMask, 15) && !values[index++].isEmpty()) {
- position.set(Position.KEY_AXLE_WEIGHT, Integer.parseInt(values[index - 1]));
+ if (BitUtil.check(reportMask, 15) && !v[index++].isEmpty()) {
+ position.set(Position.KEY_AXLE_WEIGHT, Integer.parseInt(v[index - 1]));
}
- if (BitUtil.check(reportMask, 16) && !values[index++].isEmpty()) {
- position.set("tachographInfo", Integer.parseInt(values[index - 1], 16));
+ if (BitUtil.check(reportMask, 16) && !v[index++].isEmpty()) {
+ position.set("tachographInfo", Integer.parseInt(v[index - 1], 16));
}
- if (BitUtil.check(reportMask, 17) && !values[index++].isEmpty()) {
- position.set("indicators", Integer.parseInt(values[index - 1], 16));
+ if (BitUtil.check(reportMask, 17) && !v[index++].isEmpty()) {
+ position.set("indicators", Integer.parseInt(v[index - 1], 16));
}
- if (BitUtil.check(reportMask, 18) && !values[index++].isEmpty()) {
- position.set("lights", Integer.parseInt(values[index - 1], 16));
+ if (BitUtil.check(reportMask, 18) && !v[index++].isEmpty()) {
+ position.set("lights", Integer.parseInt(v[index - 1], 16));
}
- if (BitUtil.check(reportMask, 19) && !values[index++].isEmpty()) {
- position.set("doors", Integer.parseInt(values[index - 1], 16));
+ if (BitUtil.check(reportMask, 19) && !v[index++].isEmpty()) {
+ position.set("doors", Integer.parseInt(v[index - 1], 16));
}
- if (BitUtil.check(reportMask, 20) && !values[index++].isEmpty()) {
- position.set("vehicleOverspeed", Double.parseDouble(values[index - 1]));
+ if (BitUtil.check(reportMask, 20) && !v[index++].isEmpty()) {
+ position.set("vehicleOverspeed", Double.parseDouble(v[index - 1]));
}
- if (BitUtil.check(reportMask, 21) && !values[index++].isEmpty()) {
- position.set("engineOverspeed", Double.parseDouble(values[index - 1]));
+ if (BitUtil.check(reportMask, 21) && !v[index++].isEmpty()) {
+ position.set("engineOverspeed", Double.parseDouble(v[index - 1]));
}
- if (BitUtil.check(reportMask, 29)) {
- reportMaskExt = Long.parseLong(values[index++], 16);
+ if ("GV350M".equals(model)) {
+ 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
+ }
+ } else if ("GV355CEU".equals(model)) {
+ if (BitUtil.check(reportMask, 22)) {
+ index += 1; // impulse distance
+ }
+ if (BitUtil.check(reportMask, 23)) {
+ index += 1; // engine cold starts
+ }
+ if (BitUtil.check(reportMask, 24)) {
+ index += 1; // engine all starts
+ }
+ if (BitUtil.check(reportMask, 25)) {
+ index += 1; // engine starts by ignition
+ }
+ if (BitUtil.check(reportMask, 26)) {
+ index += 1; // total engine cold running time
+ }
+ if (BitUtil.check(reportMask, 27)) {
+ index += 1; // handbrake applies during ride
+ }
+ if (BitUtil.check(reportMask, 28)) {
+ index += 1; // electric report mask
+ }
+ }
+ if (BitUtil.check(reportMask, 29) && !v[index++].isEmpty()) {
+ reportMaskExt = Long.parseLong(v[index - 1], 16);
}
- if (BitUtil.check(reportMaskExt, 0) && !values[index++].isEmpty()) {
- position.set("adBlueLevel", Integer.parseInt(values[index - 1]));
+ if (BitUtil.check(reportMaskExt, 0) && !v[index++].isEmpty()) {
+ position.set("adBlueLevel", Integer.parseInt(v[index - 1]));
}
- if (BitUtil.check(reportMaskExt, 1) && !values[index++].isEmpty()) {
- position.set("axleWeight1", Integer.parseInt(values[index - 1]));
+ if (BitUtil.check(reportMaskExt, 1) && !v[index++].isEmpty()) {
+ position.set("axleWeight1", Integer.parseInt(v[index - 1]));
}
- if (BitUtil.check(reportMaskExt, 2) && !values[index++].isEmpty()) {
- position.set("axleWeight3", Integer.parseInt(values[index - 1]));
+ if (BitUtil.check(reportMaskExt, 2) && !v[index++].isEmpty()) {
+ position.set("axleWeight3", Integer.parseInt(v[index - 1]));
}
- if (BitUtil.check(reportMaskExt, 3) && !values[index++].isEmpty()) {
- position.set("axleWeight4", Integer.parseInt(values[index - 1]));
+ if (BitUtil.check(reportMaskExt, 3) && !v[index++].isEmpty()) {
+ position.set("axleWeight4", Integer.parseInt(v[index - 1]));
}
if (BitUtil.check(reportMaskExt, 4)) {
index += 1; // tachograph overspeed
@@ -733,8 +596,8 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
if (BitUtil.check(reportMaskExt, 6)) {
index += 1; // tachograph direction
}
- if (BitUtil.check(reportMaskExt, 7) && !values[index++].isEmpty()) {
- position.set(Position.PREFIX_ADC + 1, Integer.parseInt(values[index - 1]) * 0.001);
+ if (BitUtil.check(reportMaskExt, 7) && !v[index++].isEmpty()) {
+ position.set(Position.PREFIX_ADC + 1, Integer.parseInt(v[index - 1]) * 0.001);
}
if (BitUtil.check(reportMaskExt, 8)) {
index += 1; // pedal breaking factor
@@ -757,20 +620,20 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
if (BitUtil.check(reportMaskExt, 14)) {
index += 1; // total brake application
}
- if (BitUtil.check(reportMaskExt, 15) && !values[index++].isEmpty()) {
- position.set("driver1Card", values[index - 1]);
+ if (BitUtil.check(reportMaskExt, 15) && !v[index++].isEmpty()) {
+ position.set("driver1Card", v[index - 1]);
}
- if (BitUtil.check(reportMaskExt, 16) && !values[index++].isEmpty()) {
- position.set("driver2Card", values[index - 1]);
+ if (BitUtil.check(reportMaskExt, 16) && !v[index++].isEmpty()) {
+ position.set("driver2Card", v[index - 1]);
}
- if (BitUtil.check(reportMaskExt, 17) && !values[index++].isEmpty()) {
- position.set("driver1Name", values[index - 1]);
+ if (BitUtil.check(reportMaskExt, 17) && !v[index++].isEmpty()) {
+ position.set("driver1Name", v[index - 1]);
}
- if (BitUtil.check(reportMaskExt, 18) && !values[index++].isEmpty()) {
- position.set("driver2Name", values[index - 1]);
+ if (BitUtil.check(reportMaskExt, 18) && !v[index++].isEmpty()) {
+ position.set("driver2Name", v[index - 1]);
}
- if (BitUtil.check(reportMaskExt, 19) && !values[index++].isEmpty()) {
- position.set("registration", values[index - 1]);
+ if (BitUtil.check(reportMaskExt, 19) && !v[index++].isEmpty()) {
+ position.set("registration", v[index - 1]);
}
if (BitUtil.check(reportMaskExt, 20)) {
index += 1; // expansion information
@@ -788,18 +651,18 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- if (BitUtil.check(reportMask, 30)) {
- while (values[index].isEmpty()) {
+ if (!"GV355CEU".equals(model) && BitUtil.check(reportMask, 30)) {
+ while (v[index].isEmpty()) {
index += 1;
}
- position.setValid(Integer.parseInt(values[index++]) > 0);
- if (!values[index].isEmpty()) {
- position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(values[index++])));
- position.setCourse(Integer.parseInt(values[index++]));
- position.setAltitude(Double.parseDouble(values[index++]));
- position.setLongitude(Double.parseDouble(values[index++]));
- position.setLatitude(Double.parseDouble(values[index++]));
- position.setTime(dateFormat.parse(values[index++]));
+ position.setValid(Integer.parseInt(v[index++]) > 0);
+ if (!v[index].isEmpty()) {
+ position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(v[index++])));
+ position.setCourse(Integer.parseInt(v[index++]));
+ position.setAltitude(Double.parseDouble(v[index++]));
+ position.setLongitude(Double.parseDouble(v[index++]));
+ position.setLatitude(Double.parseDouble(v[index++]));
+ position.setTime(dateFormat.parse(v[index++]));
} else {
index += 6; // no location
getLastLocation(position, null);
@@ -813,34 +676,79 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
index += 1; // reserved
}
+ index = v.length - 2;
if (ignoreFixTime) {
- position.setTime(dateFormat.parse(values[index]));
+ position.setTime(dateFormat.parse(v[index]));
} else {
- position.setDeviceTime(dateFormat.parse(values[index]));
+ position.setDeviceTime(dateFormat.parse(v[index]));
}
return position;
}
- private void decodeStatus(Position position, Parser parser) {
- if (parser.hasNext(3)) {
- int ignition = parser.nextHexInt();
- if (BitUtil.check(ignition, 4)) {
- position.set(Position.KEY_IGNITION, false);
- } else if (BitUtil.check(ignition, 5)) {
- position.set(Position.KEY_IGNITION, true);
- }
- int input = parser.nextHexInt();
- int output = parser.nextHexInt();
- position.set(Position.KEY_INPUT, input);
- position.set(Position.PREFIX_IN + 1, BitUtil.check(input, 1));
- position.set(Position.PREFIX_IN + 2, BitUtil.check(input, 2));
- position.set(Position.KEY_OUTPUT, output);
- position.set(Position.PREFIX_OUT + 1, BitUtil.check(output, 0));
- position.set(Position.PREFIX_OUT + 2, BitUtil.check(output, 1));
- }
+ private void decodeStatus(Position position, long value) {
+ long ignition = BitUtil.between(value, 2 * 8, 3 * 8);
+ if (BitUtil.check(ignition, 4)) {
+ position.set(Position.KEY_IGNITION, false);
+ } else if (BitUtil.check(ignition, 5)) {
+ position.set(Position.KEY_IGNITION, true);
+ }
+ long input = BitUtil.between(value, 8, 2 * 8);
+ long output = BitUtil.to(value, 8);
+ position.set(Position.KEY_INPUT, input);
+ position.set(Position.PREFIX_IN + 1, BitUtil.check(input, 1));
+ position.set(Position.PREFIX_IN + 2, BitUtil.check(input, 2));
+ position.set(Position.KEY_OUTPUT, output);
+ position.set(Position.PREFIX_OUT + 1, BitUtil.check(output, 0));
+ position.set(Position.PREFIX_OUT + 2, BitUtil.check(output, 1));
}
+ private static final Pattern PATTERN_FRI = new PatternBuilder()
+ .text("+").expression("(?:RESP|BUFF):GT...,")
+ .expression("(?:.{6}|.{10})?,") // 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,5})?,") // battery
+ .number("(d{1,3}),") // battery level
+ .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("(x{6})?,") // 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()) {
@@ -883,8 +791,10 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
}
if (parser.hasNext()) {
- position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
+ position.set(Position.KEY_BATTERY, parser.nextInt() * 0.001);
}
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
+ position.set(Position.PREFIX_TEMP + 1, parser.nextDouble());
if (parser.hasNext()) {
position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
@@ -894,7 +804,9 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.PREFIX_ADC + 2, parser.next());
position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
- decodeStatus(position, parser);
+ if (parser.hasNext()) {
+ decodeStatus(position, parser.nextHexLong());
+ }
position.set(Position.KEY_RPM, parser.nextInt());
position.set(Position.KEY_FUEL_LEVEL, parser.nextInt());
@@ -921,109 +833,112 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
return positions;
}
- private Object decodeEri(Channel channel, SocketAddress remoteAddress, String sentence) {
- Parser parser = new Parser(PATTERN_ERI, sentence);
- if (!parser.matches()) {
- return null;
- }
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ private Object decodeEri(Channel channel, SocketAddress remoteAddress, String[] v) throws ParseException {
+ int index = 0;
+ index += 1; // header
+ index += 1; // protocol version
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, v[index++]);
if (deviceSession == null) {
return null;
}
- long mask = parser.nextHexLong();
+ String model = getDeviceModel(deviceSession, v[index++]);
+ long mask = Long.parseLong(v[index++], 16);
+ Double power = v[index++].isEmpty() ? null : Integer.parseInt(v[index - 1]) * 0.001;
+ index += 1; // report type
+ int count = Integer.parseInt(v[index++]);
LinkedList<Position> positions = new LinkedList<>();
-
- Integer power = parser.nextInt();
-
- Parser itemParser = new Parser(PATTERN_LOCATION, parser.next());
- while (itemParser.find()) {
+ for (int i = 0; i < count; i++) {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
-
- decodeLocation(position, itemParser);
-
+ index = decodeLocation(position, model, v, index);
positions.add(position);
}
Position position = positions.getLast();
+ position.set(Position.KEY_POWER, power);
- skipLocation(parser);
-
- if (power != null) {
- position.set(Position.KEY_POWER, power * 0.001);
+ if (!model.startsWith("GL5")) {
+ position.set(Position.KEY_ODOMETER, v[index++].isEmpty() ? null : Double.parseDouble(v[index - 1]) * 1000);
+ position.set(Position.KEY_HOURS, parseHours(v[index++]));
+ position.set(Position.PREFIX_ADC + 1, v[index++].isEmpty() ? null : Integer.parseInt(v[index - 1]) * 0.001);
+ }
+ if (model.startsWith("GV") && !model.startsWith("GV6")) {
+ position.set(Position.PREFIX_ADC + 2, v[index++].isEmpty() ? null : Integer.parseInt(v[index - 1]) * 0.001);
}
- if (parser.hasNext(12)) {
-
- position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
- position.set(Position.KEY_HOURS, parseHours(parser.next()));
- position.set(Position.PREFIX_ADC + 1, parser.next());
- position.set(Position.PREFIX_ADC + 2, parser.next());
- position.set(Position.PREFIX_ADC + 3, parser.next());
- if (parser.hasNext(2)) {
- position.set(Position.KEY_INPUT, parser.nextHexInt());
- position.set(Position.KEY_OUTPUT, parser.nextHexInt());
- }
- if (parser.hasNext(4)) {
- position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
- decodeStatus(position, parser);
+ position.set(Position.KEY_BATTERY_LEVEL, v[index++].isEmpty() ? null : Integer.parseInt(v[index - 1]));
+ if (model.startsWith("GL5")) {
+ index += 1; // mode selection
+ position.set(Position.KEY_MOTION, v[index++].isEmpty() ? null : Integer.parseInt(v[index - 1]) > 0);
+ } else {
+ if (!v[index++].isEmpty()) {
+ decodeStatus(position, Long.parseLong(v[index - 1]));
}
+ index += 1; // reserved / uart device type
+ }
- int index = 0;
- String[] data = parser.next().split(",");
-
- index += 1; // device type
-
- if (BitUtil.check(mask, 0)) {
- index += 1; // digital fuel sensor data
- }
+ if (BitUtil.check(mask, 0)) {
+ position.set(Position.KEY_FUEL_LEVEL, Integer.parseInt(v[index++], 16));
+ }
- if (BitUtil.check(mask, 1)) {
- int deviceCount = Integer.parseInt(data[index++]);
- for (int i = 1; i <= deviceCount; i++) {
- index += 1; // id
- index += 1; // type
- if (!data[index++].isEmpty()) {
- position.set(Position.PREFIX_TEMP + i, (short) Integer.parseInt(data[index - 1], 16) * 0.0625);
- }
+ if (BitUtil.check(mask, 1)) {
+ int deviceCount = Integer.parseInt(v[index++]);
+ for (int i = 1; i <= deviceCount; i++) {
+ index += 1; // id
+ index += 1; // type
+ if (!v[index++].isEmpty()) {
+ position.set(Position.PREFIX_TEMP + i, (short) Integer.parseInt(v[index - 1], 16) * 0.0625);
}
}
+ }
- if (BitUtil.check(mask, 2)) {
- index += 1; // can data
- }
+ if (BitUtil.check(mask, 2)) {
+ index += 1; // can data
+ }
- if (BitUtil.check(mask, 3) || BitUtil.check(mask, 4)) {
- int deviceCount = Integer.parseInt(data[index++]);
- for (int i = 1; i <= deviceCount; i++) {
- index += 1; // type
- if (BitUtil.check(mask, 3)) {
- position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(data[index++]));
- }
- if (BitUtil.check(mask, 4)) {
- index += 1; // volume
- }
+ if (BitUtil.check(mask, 3) || BitUtil.check(mask, 4)) {
+ int deviceCount = Integer.parseInt(v[index++]);
+ for (int i = 1; i <= deviceCount; i++) {
+ index += 1; // type
+ if (BitUtil.check(mask, 3)) {
+ position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(v[index++]));
+ }
+ if (BitUtil.check(mask, 4)) {
+ index += 1; // volume
}
}
-
}
- if (parser.hasNext()) {
- position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
- }
-
- decodeDeviceTime(position, parser);
+ Date time = dateFormat.parse(v[v.length - 2]);
if (ignoreFixTime) {
+ position.setTime(time);
positions.clear();
positions.add(position);
+ } else {
+ position.setDeviceTime(time);
}
return positions;
}
+ private static final Pattern PATTERN_IGN = new PatternBuilder()
+ .text("+").expression("(?:RESP|BUFF):GTIG[NF],")
+ .expression("(?:.{6}|.{10})?,") // 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);
@@ -1042,6 +957,21 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private static final Pattern PATTERN_LSW = new PatternBuilder()
+ .text("+RESP:").expression("GT[LT]SW,")
+ .expression("(?:.{6}|.{10})?,") // 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);
@@ -1058,6 +988,24 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private static final Pattern PATTERN_IDA = new PatternBuilder()
+ .text("+RESP:GTIDA,")
+ .expression("(?:.{6}|.{10})?,") // 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);
@@ -1076,6 +1024,21 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private static final Pattern PATTERN_WIF = new PatternBuilder()
+ .text("+RESP:GTWIF,")
+ .expression("(?:.{6}|.{10})?,") // 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);
@@ -1102,6 +1065,19 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private static final Pattern PATTERN_GSM = new PatternBuilder()
+ .text("+RESP:GTGSM,")
+ .expression("(?:.{6}|.{10})?,") // 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);
@@ -1128,6 +1104,18 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private static final Pattern PATTERN_PNA = new PatternBuilder()
+ .text("+RESP:GT").expression("P[NF]A,")
+ .expression("(?:.{6}|.{10})?,") // 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);
@@ -1142,6 +1130,22 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private static final Pattern PATTERN_DAR = new PatternBuilder()
+ .text("+RESP:GTDAR,")
+ .expression("(?:.{6}|.{10})?,") // 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);
@@ -1165,6 +1169,204 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private static final Pattern PATTERN_DTT = new PatternBuilder()
+ .text("+RESP:GTDTT,")
+ .expression("(?:.{6}|.{10})?,") // 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")) {
+ position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(data.split(",")[2]));
+ } else {
+ position.set(Position.KEY_RESULT, data);
+ }
+
+ decodeDeviceTime(position, parser);
+
+ return position;
+ }
+
+ private static final Pattern PATTERN_BAA = new PatternBuilder()
+ .text("+RESP:GTBAA,")
+ .expression("(?:.{6}|.{10})?,") // 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,")
+ .expression("(?:.{6}|.{10})?,") // 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_LSA = new PatternBuilder()
+ .text("+RESP:GTLSA,")
+ .expression("(?:.{6}|.{10})?,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("[^,]*,") // device name
+ .number("d,") // event state 1
+ .number("d,") // event state 2
+ .number("d+,") // number
+ .expression(PATTERN_LOCATION.pattern())
+ .number("d+,") // bit error rate
+ .number("(d),") // light level
+ .number("(d+),") // battery level
+ .number("[01],") // mode selection
+ .number("[01]?,") // movement status
+ .number("(-?d+.d)?,") // temperature
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private Object decodeLsa(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_LSA, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ decodeLocation(position, parser);
+
+ position.set("lightLevel", parser.nextInt());
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
+ position.set(Position.PREFIX_TEMP + 1, parser.nextDouble());
+
+ decodeDeviceTime(position, parser);
+
+ return position;
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("+").expression("(?:RESP|BUFF):GT...,")
+ .expression("(?:.{6}|.{10})?,") // 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);
@@ -1215,6 +1417,38 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private static final Pattern PATTERN_BASIC = new PatternBuilder()
+ .text("+").expression("(?:RESP|BUFF)").text(":")
+ .expression("GT...,")
+ .expression("[^,]+,").optional() // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .any()
+ .text(",")
+ .number("(d{1,2}),") // hdop
+ .groupBegin()
+ .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)") // time (hhmmss)
+ .text(",")
+ .or()
+ .text(",,,,,,")
+ .groupEnd()
+ .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);
@@ -1299,17 +1533,19 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- String sentence = ((ByteBuf) msg).toString(StandardCharsets.US_ASCII);
+ String sentence = ((ByteBuf) msg).toString(StandardCharsets.US_ASCII).replaceAll("\\$$", "");
int typeIndex = sentence.indexOf(":GT");
if (typeIndex < 0) {
return null;
}
+ String[] values = sentence.split(",");
+
Object result;
String type = sentence.substring(typeIndex + 3, typeIndex + 6);
if (sentence.startsWith("+ACK")) {
- result = decodeAck(channel, remoteAddress, sentence, type);
+ result = decodeAck(channel, remoteAddress, values);
} else {
switch (type) {
case "INF":
@@ -1319,7 +1555,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
result = decodeObd(channel, remoteAddress, sentence);
break;
case "CAN":
- result = decodeCan(channel, remoteAddress, sentence);
+ result = decodeCan(channel, remoteAddress, values);
break;
case "CTN":
case "FRI":
@@ -1330,7 +1566,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
result = decodeFri(channel, remoteAddress, sentence);
break;
case "ERI":
- result = decodeEri(channel, remoteAddress, sentence);
+ result = decodeEri(channel, remoteAddress, values);
break;
case "IGN":
case "IGF":
@@ -1359,6 +1595,18 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
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;
+ case "LSA":
+ result = decodeLsa(channel, remoteAddress, sentence);
+ break;
default:
result = decodeOther(channel, remoteAddress, sentence, type);
break;
@@ -1380,13 +1628,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
}
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);
- } else {
- checksum = sentence.substring(sentence.length() - 4);
- }
- channel.writeAndFlush(new NetworkMessage("+SACK:" + checksum + "$", remoteAddress));
+ channel.writeAndFlush(new NetworkMessage("+SACK:" + values[values.length - 1] + "$", remoteAddress));
}
return result;
diff --git a/src/main/java/org/traccar/protocol/GlobalSatProtocol.java b/src/main/java/org/traccar/protocol/GlobalSatProtocol.java
index 16b99f426..13f4f2646 100644
--- a/src/main/java/org/traccar/protocol/GlobalSatProtocol.java
+++ b/src/main/java/org/traccar/protocol/GlobalSatProtocol.java
@@ -24,7 +24,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class GlobalSatProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/GlobalstarProtocol.java b/src/main/java/org/traccar/protocol/GlobalstarProtocol.java
index 293f5fda5..1d9b6b6dd 100644
--- a/src/main/java/org/traccar/protocol/GlobalstarProtocol.java
+++ b/src/main/java/org/traccar/protocol/GlobalstarProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class GlobalstarProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java b/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java
index 0ddb95c14..b75e612b8 100644
--- a/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 - 2020 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.
@@ -27,6 +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.config.Keys;
import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
@@ -64,6 +65,12 @@ public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder {
private final XPath xPath;
private final XPathExpression messageExpression;
+ private boolean alternative;
+
+ public void setAlternative(boolean alternative) {
+ this.alternative = alternative;
+ }
+
public GlobalstarProtocolDecoder(Protocol protocol) {
super(protocol);
try {
@@ -82,6 +89,11 @@ public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder {
}
}
+ @Override
+ protected void init() {
+ this.alternative = getConfig().getBoolean(Keys.PROTOCOL_ALTERNATIVE.withPrefix(getProtocolName()));
+ }
+
private void sendResponse(Channel channel, String messageId) throws TransformerException {
Document document = documentBuilder.newDocument();
@@ -135,32 +147,50 @@ public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- position.setValid(true);
position.setTime(new Date(Long.parseLong(xPath.evaluate("unixTime", node)) * 1000));
ByteBuf buf = Unpooled.wrappedBuffer(
DataConverter.parseHex(xPath.evaluate("payload", node).substring(2)));
int flags = buf.readUnsignedByte();
- position.set(Position.PREFIX_IN + 1, !BitUtil.check(flags, 1));
- position.set(Position.PREFIX_IN + 2, !BitUtil.check(flags, 2));
- position.set(Position.KEY_CHARGE, !BitUtil.check(flags, 3));
- if (BitUtil.check(flags, 4)) {
- position.set(Position.KEY_ALARM, Position.ALARM_VIBRATION);
+ int type;
+ if (alternative) {
+ type = BitUtil.to(flags, 1);
+ position.setValid(true);
+ position.set(Position.PREFIX_IN + 1, !BitUtil.check(flags, 1));
+ position.set(Position.PREFIX_IN + 2, !BitUtil.check(flags, 2));
+ position.set(Position.KEY_CHARGE, !BitUtil.check(flags, 3));
+ if (BitUtil.check(flags, 4)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_VIBRATION);
+ }
+ position.setCourse(BitUtil.from(flags, 5) * 45);
+ } else {
+ type = BitUtil.to(flags, 2);
+ if (BitUtil.check(flags, 2)) {
+ position.set("batteryReplace", true);
+ }
+ position.setValid(!BitUtil.check(flags, 3));
}
- position.setCourse(BitUtil.from(flags, 5) * 45);
-
double latitude = buf.readUnsignedMedium() * 90.0 / (1 << 23);
position.setLatitude(latitude > 90 ? latitude - 180 : latitude);
double longitude = buf.readUnsignedMedium() * 180.0 / (1 << 23);
position.setLongitude(longitude > 180 ? longitude - 360 : longitude);
- int speed = buf.readUnsignedByte();
- position.setSpeed(UnitsConverter.knotsFromKph(speed));
-
- position.set("batteryReplace", BitUtil.check(buf.readUnsignedByte(), 7));
+ int speed = 0;
+ if (alternative) {
+ speed = buf.readUnsignedByte();
+ position.setSpeed(UnitsConverter.knotsFromKph(speed));
+ position.set("batteryReplace", BitUtil.check(buf.readUnsignedByte(), 7));
+ } else if (type == 0) {
+ position.set(Position.KEY_INPUT, BitUtil.to(buf.readUnsignedByte(), 4));
+ int other = buf.readUnsignedByte();
+ if (BitUtil.check(other, 4)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_VIBRATION);
+ }
+ position.set(Position.KEY_MOTION, BitUtil.check(other, 6));
+ }
if (speed != 0xff) {
positions.add(position);
diff --git a/src/main/java/org/traccar/protocol/GnxProtocol.java b/src/main/java/org/traccar/protocol/GnxProtocol.java
index 32d642688..cfa496009 100644
--- a/src/main/java/org/traccar/protocol/GnxProtocol.java
+++ b/src/main/java/org/traccar/protocol/GnxProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class GnxProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/GoSafeProtocol.java b/src/main/java/org/traccar/protocol/GoSafeProtocol.java
index 607931500..c9c0456a0 100644
--- a/src/main/java/org/traccar/protocol/GoSafeProtocol.java
+++ b/src/main/java/org/traccar/protocol/GoSafeProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class GoSafeProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/GoSafeProtocolDecoder.java b/src/main/java/org/traccar/protocol/GoSafeProtocolDecoder.java
index 77649a041..f17ea0e08 100644
--- a/src/main/java/org/traccar/protocol/GoSafeProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GoSafeProtocolDecoder.java
@@ -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 53fcea0d0..21fbbae99 100644
--- a/src/main/java/org/traccar/protocol/GotopProtocol.java
+++ b/src/main/java/org/traccar/protocol/GotopProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class GotopProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Gps056Protocol.java b/src/main/java/org/traccar/protocol/Gps056Protocol.java
index dbffbfdbb..44fc392be 100644
--- a/src/main/java/org/traccar/protocol/Gps056Protocol.java
+++ b/src/main/java/org/traccar/protocol/Gps056Protocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Gps056Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Gps103Protocol.java b/src/main/java/org/traccar/protocol/Gps103Protocol.java
index 2725494c5..8424abfe5 100644
--- a/src/main/java/org/traccar/protocol/Gps103Protocol.java
+++ b/src/main/java/org/traccar/protocol/Gps103Protocol.java
@@ -24,7 +24,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Gps103Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java
index 28efa3c30..d1c35b478 100644
--- a/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java
@@ -225,7 +225,7 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder {
getConfig(), parser.nextHexInt(0), parser.nextHexInt(0))));
}
- if (parser.hasNext(20)) {
+ if (parser.hasNextAny(20)) {
String utcHours = parser.next();
String utcMinutes = parser.next();
diff --git a/src/main/java/org/traccar/protocol/GpsGateProtocol.java b/src/main/java/org/traccar/protocol/GpsGateProtocol.java
index a6a73ae6b..db1e8554a 100644
--- a/src/main/java/org/traccar/protocol/GpsGateProtocol.java
+++ b/src/main/java/org/traccar/protocol/GpsGateProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class GpsGateProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/GpsMarkerProtocol.java b/src/main/java/org/traccar/protocol/GpsMarkerProtocol.java
index 12b53342c..f50088b2b 100644
--- a/src/main/java/org/traccar/protocol/GpsMarkerProtocol.java
+++ b/src/main/java/org/traccar/protocol/GpsMarkerProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class GpsMarkerProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/GpsmtaProtocol.java b/src/main/java/org/traccar/protocol/GpsmtaProtocol.java
index a474b1e53..e146a816d 100644
--- a/src/main/java/org/traccar/protocol/GpsmtaProtocol.java
+++ b/src/main/java/org/traccar/protocol/GpsmtaProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class GpsmtaProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/GranitProtocol.java b/src/main/java/org/traccar/protocol/GranitProtocol.java
index bb66501e2..9ca0fe25e 100644
--- a/src/main/java/org/traccar/protocol/GranitProtocol.java
+++ b/src/main/java/org/traccar/protocol/GranitProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class GranitProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Gs100Protocol.java b/src/main/java/org/traccar/protocol/Gs100Protocol.java
index 425ca9330..715d48fc4 100644
--- a/src/main/java/org/traccar/protocol/Gs100Protocol.java
+++ b/src/main/java/org/traccar/protocol/Gs100Protocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Gs100Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Gt02Protocol.java b/src/main/java/org/traccar/protocol/Gt02Protocol.java
index fa05761f6..f448feacc 100644
--- a/src/main/java/org/traccar/protocol/Gt02Protocol.java
+++ b/src/main/java/org/traccar/protocol/Gt02Protocol.java
@@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Gt02Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Gt06Protocol.java b/src/main/java/org/traccar/protocol/Gt06Protocol.java
index 38278121c..945ec3831 100644
--- a/src/main/java/org/traccar/protocol/Gt06Protocol.java
+++ b/src/main/java/org/traccar/protocol/Gt06Protocol.java
@@ -21,7 +21,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Gt06Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
index 5b639ddfc..6c0380278 100644
--- a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2024 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
+import org.traccar.helper.BufferUtil;
import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
@@ -77,11 +78,12 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
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
- public static final int MSG_GPS_LBS_STATUS_4 = 0x32; // AZ735
- public static final int MSG_WIFI_5 = 0x33; // AZ735
- public static final int MSG_AZ735_GPS = 0x32; // AZ735 / only extended
- public static final int MSG_AZ735_ALARM = 0x33; // AZ735 / only extended
+ 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;
@@ -95,7 +97,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
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; // GK310
+ 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
@@ -119,6 +121,10 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
SPACE10X,
STANDARD,
OBD6,
+ WETRUST,
+ JC400,
+ SL4X,
+ SEEWORLD,
}
private Variant variant;
@@ -168,7 +174,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;
@@ -190,7 +196,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:
@@ -267,12 +273,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())
@@ -291,7 +297,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();
@@ -337,21 +344,21 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
int mcc = buf.readUnsignedShort();
int mnc;
- if (BitUtil.check(mcc, 15) || type == MSG_GPS_LBS_6) {
+ if (BitUtil.check(mcc, 15) || type == MSG_GPS_LBS_6 || variant == Variant.SL4X) {
mnc = buf.readUnsignedShort();
} else {
mnc = buf.readUnsignedByte();
}
int lac;
- if (type == MSG_LBS_ALARM) {
+ if (type == MSG_LBS_ALARM || type == MSG_GPS_LBS_7) {
lac = buf.readInt();
} else {
lac = buf.readUnsignedShort();
}
long cid;
- if (type == MSG_LBS_ALARM) {
+ if (type == MSG_LBS_ALARM || type == MSG_GPS_LBS_7) {
cid = buf.readLong();
- } else if (type == MSG_GPS_LBS_6) {
+ } else if (type == MSG_GPS_LBS_6 || variant == Variant.SEEWORLD) {
cid = buf.readUnsignedInt();
} else {
cid = buf.readUnsignedMedium();
@@ -434,15 +441,18 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
return Position.ALARM_REMOVING;
case 0x23:
return Position.ALARM_FALL_DOWN;
+ case 0x28:
+ return Position.ALARM_BRAKING;
case 0x29:
return Position.ALARM_ACCELERATION;
- case 0x30:
- return Position.ALARM_BRAKING;
case 0x2A:
case 0x2B:
+ case 0x2E:
return Position.ALARM_CORNERING;
case 0x2C:
return Position.ALARM_ACCIDENT;
+ case 0x30:
+ return Position.ALARM_JAMMING;
default:
return null;
}
@@ -473,28 +483,21 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedShort(); // type
deviceSession = getDeviceSession(channel, remoteAddress, imei);
- if (deviceSession != null && !deviceSession.contains(DeviceSession.KEY_TIMEZONE)) {
- deviceSession.set(DeviceSession.KEY_TIMEZONE, getTimeZone(deviceSession.getDeviceId()));
- }
-
- if (dataLength > 10) {
- int extensionBits = buf.readUnsignedShort();
- int hours = (extensionBits >> 4) / 100;
- int minutes = (extensionBits >> 4) % 100;
- int offset = (hours * 60 + minutes) * 60;
- if ((extensionBits & 0x8) != 0) {
- offset = -offset;
- }
- if (deviceSession != null) {
- TimeZone timeZone = deviceSession.get(DeviceSession.KEY_TIMEZONE);
- if (timeZone.getRawOffset() == 0) {
- timeZone.setRawOffset(offset * 1000);
- deviceSession.set(DeviceSession.KEY_TIMEZONE, timeZone);
+ if (deviceSession != null) {
+ TimeZone timeZone = getTimeZone(deviceSession.getDeviceId(), null);
+ if (timeZone == null && dataLength > 10) {
+ int extensionBits = buf.readUnsignedShort();
+ int hours = (extensionBits >> 4) / 100;
+ int minutes = (extensionBits >> 4) % 100;
+ int offset = (hours * 60 + minutes) * 60;
+ if ((extensionBits & 0x8) != 0) {
+ offset = -offset;
}
+ timeZone = TimeZone.getTimeZone("UTC");
+ timeZone.setRawOffset(offset * 1000);
}
- }
+ deviceSession.set(DeviceSession.KEY_TIMEZONE, timeZone);
- if (deviceSession != null) {
sendResponse(channel, false, type, buf.getShort(buf.writerIndex() - 6), null);
}
@@ -545,7 +548,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
return null;
- } else if (type == MSG_X1_GPS) {
+ } else if (type == MSG_X1_GPS && variant != Variant.SL4X) {
buf.readUnsignedInt(); // data and alarm
@@ -678,41 +681,50 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
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((TimeZone) deviceSession.get(DeviceSession.KEY_TIMEZONE))
.setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
.setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
getLastLocation(position, dateBuilder.getDate());
- if (variant == Variant.WANWAY_S20) {
+ 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 = 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 (variant != Variant.WANWAY_S20) {
+ 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:");
@@ -805,7 +817,11 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
}
if (hasLbs(type) && buf.readableBytes() > 6) {
- decodeLbs(position, buf, type, hasStatus(type) && type != MSG_LBS_ALARM && type != MSG_LBS_STATUS);
+ 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)) {
@@ -821,9 +837,13 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedByte(); // working mode
position.set(Position.KEY_POWER, buf.readUnsignedShort() / 100.0);
} else {
- position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte() * 100 / 6);
+ int battery = buf.readUnsignedByte();
+ position.set(Position.KEY_BATTERY_LEVEL, battery <= 6 ? battery * 100 / 6 : battery);
position.set(Position.KEY_RSSI, buf.readUnsignedByte());
- position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
+ short alarmExtension = buf.readUnsignedByte();
+ if (variant != Variant.VXT01) {
+ position.set(Position.KEY_ALARM, decodeAlarm(alarmExtension));
+ }
}
}
@@ -833,7 +853,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
String data = buf.readCharSequence(buf.readUnsignedByte(), StandardCharsets.US_ASCII).toString();
buf.readUnsignedByte(); // alarm
buf.readUnsignedByte(); // swiped
- position.set("driverLicense", data.trim());
+ 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));
@@ -869,9 +889,30 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
}
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 && variant == Variant.SEEWORLD) {
+ position.set(Position.KEY_IGNITION, buf.readUnsignedByte() > 0);
+ buf.readUnsignedByte(); // reporting mode
+ buf.readUnsignedByte(); // supplementary transmission
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+ buf.readUnsignedInt(); // travel time
+ int temperature = buf.readUnsignedShort();
+ if (BitUtil.check(temperature, 15)) {
+ temperature = -BitUtil.to(temperature, 15);
+ }
+ position.set(Position.PREFIX_TEMP + 1, temperature * 0.01);
+ position.set("humidity", buf.readUnsignedShort() * 0.01);
+ }
+
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);
@@ -898,6 +939,12 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
}
}
+ 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());
}
@@ -906,24 +953,44 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
boolean extendedAlarm = dataLength > 7;
if (extendedAlarm) {
- decodeGps(position, buf, false, false, false, deviceSession.get(DeviceSession.KEY_TIMEZONE));
+ 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((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;
@@ -937,7 +1004,6 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ALARM, Position.ALARM_ACCIDENT);
break;
default:
- position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
break;
}
@@ -1020,6 +1086,29 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
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) {
@@ -1339,19 +1428,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;
@@ -1391,6 +1474,18 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
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 if (header == 0x7878 && type == MSG_GPS_LBS_STATUS_4 && length == 0x27) {
+ variant = Variant.SL4X;
+ } else if (header == 0x7878 && type == MSG_GPS_LBS_2 && length == 0x2f) {
+ variant = Variant.SEEWORLD;
+ } else if (header == 0x7878 && type == MSG_GPS_LBS_STATUS_1 && length == 0x26) {
+ variant = Variant.SEEWORLD;
} 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 dc5dd446f..fd6bb8451 100644
--- a/src/main/java/org/traccar/protocol/Gt06ProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/Gt06ProtocolEncoder.java
@@ -23,7 +23,6 @@ import org.traccar.config.Keys;
import org.traccar.helper.Checksum;
import org.traccar.helper.model.AttributeUtil;
import org.traccar.model.Command;
-import org.traccar.model.Device;
import java.nio.charset.StandardCharsets;
@@ -74,11 +73,11 @@ public class Gt06ProtocolEncoder extends BaseProtocolEncoder {
String password = AttributeUtil.getDevicePassword(
getCacheManager(), command.getDeviceId(), getProtocolName(), "123456");
- Device device = getCacheManager().getObject(Device.class, command.getDeviceId());
+ String model = getDeviceModel(command.getDeviceId());
switch (command.getType()) {
case Command.TYPE_ENGINE_STOP:
- if ("G109".equals(device.getModel())) {
+ if ("G109".equals(model)) {
return encodeContent(command.getDeviceId(), "DYD#");
} else if (alternative) {
return encodeContent(command.getDeviceId(), "DYD," + password + "#");
@@ -86,7 +85,7 @@ public class Gt06ProtocolEncoder extends BaseProtocolEncoder {
return encodeContent(command.getDeviceId(), "Relay,1#");
}
case Command.TYPE_ENGINE_RESUME:
- if ("G109".equals(device.getModel())) {
+ if ("G109".equals(model)) {
return encodeContent(command.getDeviceId(), "HFYD#");
} else if (alternative) {
return encodeContent(command.getDeviceId(), "HFYD," + password + "#");
diff --git a/src/main/java/org/traccar/protocol/Gt30Protocol.java b/src/main/java/org/traccar/protocol/Gt30Protocol.java
index 6b79ba58b..fdfc80502 100644
--- a/src/main/java/org/traccar/protocol/Gt30Protocol.java
+++ b/src/main/java/org/traccar/protocol/Gt30Protocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Gt30Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/H02Protocol.java b/src/main/java/org/traccar/protocol/H02Protocol.java
index 4e5f8c96a..ba5aeaa26 100644
--- a/src/main/java/org/traccar/protocol/H02Protocol.java
+++ b/src/main/java/org/traccar/protocol/H02Protocol.java
@@ -23,7 +23,7 @@ import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class H02Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/HaicomProtocol.java b/src/main/java/org/traccar/protocol/HaicomProtocol.java
index f56c605f0..bcc491ada 100644
--- a/src/main/java/org/traccar/protocol/HaicomProtocol.java
+++ b/src/main/java/org/traccar/protocol/HaicomProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class HaicomProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/HomtecsProtocol.java b/src/main/java/org/traccar/protocol/HomtecsProtocol.java
index aa2d7d852..c04efb945 100644
--- a/src/main/java/org/traccar/protocol/HomtecsProtocol.java
+++ b/src/main/java/org/traccar/protocol/HomtecsProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class HomtecsProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/HoopoProtocol.java b/src/main/java/org/traccar/protocol/HoopoProtocol.java
index 02d8e5a8e..3fc0887d8 100644
--- a/src/main/java/org/traccar/protocol/HoopoProtocol.java
+++ b/src/main/java/org/traccar/protocol/HoopoProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class HoopoProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/HoopoProtocolDecoder.java b/src/main/java/org/traccar/protocol/HoopoProtocolDecoder.java
index 708c74f2a..7433e7fce 100644
--- a/src/main/java/org/traccar/protocol/HoopoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/HoopoProtocolDecoder.java
@@ -21,8 +21,8 @@ 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 b1b61e977..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.
@@ -19,17 +19,25 @@ import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
+import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class HuaShengProtocol extends BaseProtocol {
@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, 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 371691d82..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.
@@ -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 c37918b0e..fc12d7d71 100644
--- a/src/main/java/org/traccar/protocol/HuabaoProtocol.java
+++ b/src/main/java/org/traccar/protocol/HuabaoProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class HuabaoProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
index d0bbeebb5..0a8540543 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 - 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.
@@ -131,7 +131,10 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
if (BitUtil.check(value, 8)) {
return Position.ALARM_POWER_OFF;
}
- if (BitUtil.check(value, 17)) {
+ if (BitUtil.check(value, 15)) {
+ return Position.ALARM_VIBRATION;
+ }
+ if (BitUtil.check(value, 16) || BitUtil.check(value, 17)) {
return Position.ALARM_TAMPERING;
}
if (BitUtil.check(value, 20)) {
@@ -140,7 +143,7 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
if (BitUtil.check(value, 28)) {
return Position.ALARM_MOVEMENT;
}
- if (BitUtil.check(value, 29)) {
+ if (BitUtil.check(value, 29) || BitUtil.check(value, 30)) {
return Position.ALARM_ACCIDENT;
}
return null;
@@ -169,7 +172,7 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
} else {
long imei = id.getUnsignedShort(0);
imei = (imei << 32) + id.getUnsignedInt(2);
- return String.valueOf(imei);
+ return String.valueOf(imei) + Checksum.luhn(imei);
}
}
@@ -294,6 +297,8 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
} else if (type == MSG_TRANSPARENT) {
+ sendGeneralResponse(channel, remoteAddress, id, type, index);
+
return decodeTransparent(deviceSession, buf);
}
@@ -327,6 +332,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;
@@ -388,6 +403,7 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
int status = buf.readInt();
position.set(Position.KEY_IGNITION, BitUtil.check(status, 0));
+ position.set(Position.KEY_MOTION, BitUtil.check(status, 4));
position.set(Position.KEY_BLOCKED, BitUtil.check(status, 10));
position.set(Position.KEY_CHARGE, BitUtil.check(status, 26));
@@ -448,6 +464,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);
@@ -455,8 +472,12 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
case 0x02:
position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedShort() * 0.1);
break;
+ case 0x25:
+ position.set(Position.KEY_INPUT, buf.readUnsignedInt());
+ break;
case 0x2b:
- position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedInt());
+ position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort());
+ position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShort());
break;
case 0x30:
position.set(Position.KEY_RSSI, buf.readUnsignedByte());
@@ -465,12 +486,75 @@ 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 0x57:
+ int alarm = buf.readUnsignedShort();
+ position.set(Position.KEY_ALARM, BitUtil.check(alarm, 8) ? Position.ALARM_ACCELERATION : null);
+ position.set(Position.KEY_ALARM, BitUtil.check(alarm, 9) ? Position.ALARM_BRAKING : null);
+ position.set(Position.KEY_ALARM, BitUtil.check(alarm, 10) ? Position.ALARM_CORNERING : null);
+ buf.readUnsignedShort(); // external switch state
+ buf.skipBytes(4); // reserved
+ break;
+ case 0x60:
+ int event = buf.readUnsignedShort();
+ position.set(Position.KEY_EVENT, event);
+ if (event >= 0x0061 && event <= 0x0066) {
+ buf.skipBytes(6); // lock id
+ stringValue = buf.readCharSequence(8, StandardCharsets.US_ASCII).toString();
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, stringValue);
+ }
+ break;
+ case 0x63:
+ for (int i = 1; i <= length / 11; i++) {
+ position.set("lock" + i + "Id", ByteBufUtil.hexDump(buf.readSlice(6)));
+ position.set("lock" + i + "Battery", buf.readUnsignedShort() * 0.001);
+ position.set("lock" + i + "Seal", buf.readUnsignedByte() == 0x31);
+ buf.readUnsignedByte(); // physical state
+ buf.readUnsignedByte(); // rssi
+ }
+ break;
+ case 0x64:
+ buf.readUnsignedInt(); // alarm serial number
+ buf.readUnsignedByte(); // alarm status
+ position.set("adasAlarm", buf.readUnsignedByte());
+ break;
+ case 0x65:
+ buf.readUnsignedInt(); // alarm serial number
+ buf.readUnsignedByte(); // alarm status
+ position.set("dmsAlarm", buf.readUnsignedByte());
+ break;
+ case 0x70:
+ buf.readUnsignedInt(); // alarm serial number
+ buf.readUnsignedByte(); // alarm status
+ switch (buf.readUnsignedByte()) {
+ case 0x01:
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
+ break;
+ case 0x02:
+ position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
+ break;
+ case 0x03:
+ position.set(Position.KEY_ALARM, Position.ALARM_CORNERING);
+ break;
+ case 0x16:
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCIDENT);
+ break;
+ default:
+ break;
+ }
+ break;
+ case 0x69:
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.01);
+ break;
case 0x80:
buf.readUnsignedByte(); // content
endIndex = buf.writerIndex() - 2;
@@ -492,8 +576,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:
@@ -503,6 +587,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)) {
@@ -513,7 +605,12 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.1);
break;
case 0xD4:
- position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
+ case 0xE1:
+ if (length == 1) {
+ position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
+ } else {
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, String.valueOf(buf.readUnsignedInt()));
+ }
break;
case 0xD5:
if (length == 2) {
@@ -536,6 +633,9 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_MOTION, BitUtil.check(deviceStatus, 2));
position.set("cover", BitUtil.check(deviceStatus, 3));
break;
+ case 0xE2:
+ position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedInt() * 0.1);
+ break;
case 0xE6:
while (buf.readerIndex() < endIndex) {
int sensorIndex = buf.readUnsignedByte();
@@ -589,8 +689,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());
@@ -663,6 +763,8 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
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) {
@@ -721,12 +823,15 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
int battery = buf.readUnsignedByte();
if (battery <= 100) {
position.set(Position.KEY_BATTERY_LEVEL, battery);
- } else if (battery == 0xAA) {
+ } else if (battery == 0xAA || battery == 0xAB) {
position.set(Position.KEY_CHARGE, true);
}
- position.setNetwork(new Network(CellTower.fromCidLac(
- getConfig(), buf.readUnsignedInt(), buf.readUnsignedShort())));
+ long cid = buf.readUnsignedInt();
+ int lac = buf.readUnsignedShort();
+ if (cid > 0 && lac > 0) {
+ position.setNetwork(new Network(CellTower.fromCidLac(getConfig(), cid, lac)));
+ }
int product = buf.readUnsignedByte();
int status = buf.readUnsignedShort();
@@ -738,6 +843,9 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
}
} else if (product == 3) {
position.set(Position.KEY_BLOCKED, BitUtil.check(status, 5));
+ if (BitUtil.check(alarm, 0)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED);
+ }
if (BitUtil.check(alarm, 1)) {
position.set(Position.KEY_ALARM, Position.ALARM_LOW_POWER);
}
@@ -747,10 +855,69 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
if (BitUtil.check(alarm, 3)) {
position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY);
}
+ if (BitUtil.check(alarm, 5)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_GEOFENCE_ENTER);
+ }
+ if (BitUtil.check(alarm, 6)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_GEOFENCE_EXIT);
+ }
}
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 0x10:
+ position.set("wakeSource", buf.readUnsignedByte());
+ break;
+ case 0x0A:
+ if (length == 3) {
+ buf.readUnsignedShort(); // mcc
+ buf.readUnsignedByte(); // mnc
+ } else {
+ buf.skipBytes(length);
+ }
+ break;
+ case 0x0B:
+ position.set("lockCommand", buf.readUnsignedByte());
+ if (length >= 5 && length <= 6) {
+ position.set("lockCard", buf.readUnsignedInt());
+ } else if (length >= 7) {
+ position.set("lockPassword", buf.readCharSequence(6, StandardCharsets.US_ASCII).toString());
+ }
+ if (length % 2 == 0) {
+ position.set("unlockResult", buf.readUnsignedByte());
+ }
+ 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;
+ case 0xFC:
+ position.set(Position.KEY_GEOFENCE, buf.readUnsignedByte());
+ break;
+ default:
+ buf.skipBytes(length);
+ break;
+ }
+ }
+
return position;
}
@@ -782,6 +949,7 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
int type = buf.readUnsignedByte();
if (type == 0xF0) {
+
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
@@ -823,6 +991,34 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
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:
@@ -841,15 +1037,40 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
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.readUnsignedShort();
+ 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;
@@ -867,11 +1088,21 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
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;
@@ -886,6 +1117,24 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
}
return position;
+
+ } else if (type == 0xFF) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setValid(true);
+ position.setTime(readDate(buf, deviceSession.get(DeviceSession.KEY_TIMEZONE)));
+ position.setLatitude(buf.readInt() * 0.000001);
+ position.setLongitude(buf.readInt() * 0.000001);
+ position.setAltitude(buf.readShort());
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort() * 0.1));
+ position.setCourse(buf.readUnsignedShort());
+
+ // TODO more positions and g sensor data
+
+ return position;
+
}
return null;
diff --git a/src/main/java/org/traccar/protocol/HunterProProtocol.java b/src/main/java/org/traccar/protocol/HunterProProtocol.java
index ed4289d73..64dab33b1 100644
--- a/src/main/java/org/traccar/protocol/HunterProProtocol.java
+++ b/src/main/java/org/traccar/protocol/HunterProProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class HunterProProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/IdplProtocol.java b/src/main/java/org/traccar/protocol/IdplProtocol.java
index aa1f4ff5b..1e44ad74c 100644
--- a/src/main/java/org/traccar/protocol/IdplProtocol.java
+++ b/src/main/java/org/traccar/protocol/IdplProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class IdplProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/IntellitracProtocol.java b/src/main/java/org/traccar/protocol/IntellitracProtocol.java
index b1a91cca9..a82e6a5db 100644
--- a/src/main/java/org/traccar/protocol/IntellitracProtocol.java
+++ b/src/main/java/org/traccar/protocol/IntellitracProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class IntellitracProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/IotmProtocol.java b/src/main/java/org/traccar/protocol/IotmProtocol.java
index 0d288f4bf..1631b67d8 100644
--- a/src/main/java/org/traccar/protocol/IotmProtocol.java
+++ b/src/main/java/org/traccar/protocol/IotmProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class IotmProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/IotmProtocolDecoder.java b/src/main/java/org/traccar/protocol/IotmProtocolDecoder.java
index 7bbe6c8de..d9e6670c6 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 - 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.
@@ -17,27 +17,19 @@ package org.traccar.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
-import io.netty.channel.Channel;
-import io.netty.handler.codec.mqtt.MqttConnectMessage;
-import io.netty.handler.codec.mqtt.MqttConnectReturnCode;
-import io.netty.handler.codec.mqtt.MqttMessage;
-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.session.DeviceSession;
-import org.traccar.NetworkMessage;
+import org.traccar.BaseMqttProtocolDecoder;
import org.traccar.Protocol;
import org.traccar.helper.UnitsConverter;
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 IotmProtocolDecoder extends BaseProtocolDecoder {
+public class IotmProtocolDecoder extends BaseMqttProtocolDecoder {
public IotmProtocolDecoder(Protocol protocol) {
super(protocol);
@@ -236,121 +228,72 @@ public class IotmProtocolDecoder extends BaseProtocolDecoder {
@Override
protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
-
- if (msg instanceof MqttConnectMessage) {
-
- MqttConnectMessage message = (MqttConnectMessage) msg;
+ DeviceSession deviceSession, MqttPublishMessage message) throws Exception {
- DeviceSession deviceSession = getDeviceSession(
- channel, remoteAddress, message.payload().clientIdentifier());
-
- MqttConnectReturnCode returnCode = deviceSession != null
- ? MqttConnectReturnCode.CONNECTION_ACCEPTED
- : MqttConnectReturnCode.CONNECTION_REFUSED_IDENTIFIER_REJECTED;
-
- MqttMessage response = MqttMessageBuilders.connAck().returnCode(returnCode).build();
-
- if (channel != null) {
- channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
- }
+ List<Position> positions = new LinkedList<>();
- } else if (msg instanceof MqttSubscribeMessage) {
+ ByteBuf buf = message.payload();
- MqttSubscribeMessage message = (MqttSubscribeMessage) msg;
-
- MqttMessage response = MqttMessageBuilders.subAck()
- .packetId(message.variableHeader().messageId())
- .build();
-
- if (channel != null) {
- channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
- }
-
- } else if (msg instanceof MqttPublishMessage) {
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
- if (deviceSession == null) {
- return null;
- }
+ buf.readUnsignedByte(); // structure version
- List<Position> positions = new LinkedList<>();
+ while (buf.readableBytes() > 1) {
+ int type = buf.readUnsignedByte();
+ int length = buf.readUnsignedShortLE();
+ ByteBuf record = buf.readSlice(length);
+ if (type == 1) {
- MqttPublishMessage message = (MqttPublishMessage) msg;
- ByteBuf buf = message.payload();
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ position.setTime(new Date(record.readUnsignedIntLE() * 1000));
- buf.readUnsignedByte(); // structure version
+ while (record.readableBytes() > 0) {
+ int sensorType = record.readUnsignedByte();
+ int sensorId = record.readUnsignedShortLE();
+ if (sensorType == 14) {
- while (buf.readableBytes() > 1) {
- int type = buf.readUnsignedByte();
- int length = buf.readUnsignedShortLE();
- ByteBuf record = buf.readSlice(length);
- if (type == 1) {
+ position.setValid(true);
+ position.setLatitude(record.readFloatLE());
+ position.setLongitude(record.readFloatLE());
+ position.setSpeed(UnitsConverter.knotsFromKph(record.readUnsignedShortLE()));
- Position position = new Position(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
- position.setTime(new Date(record.readUnsignedIntLE() * 1000));
+ position.set(Position.KEY_HDOP, record.readUnsignedByte());
+ position.set(Position.KEY_SATELLITES, record.readUnsignedByte());
- while (record.readableBytes() > 0) {
- int sensorType = record.readUnsignedByte();
- int sensorId = record.readUnsignedShortLE();
- if (sensorType == 14) {
+ position.setCourse(record.readUnsignedShortLE());
+ position.setAltitude(record.readShortLE());
- position.setValid(true);
- position.setLatitude(record.readFloatLE());
- position.setLongitude(record.readFloatLE());
- position.setSpeed(UnitsConverter.knotsFromKph(record.readUnsignedShortLE()));
-
- position.set(Position.KEY_HDOP, record.readUnsignedByte());
- position.set(Position.KEY_SATELLITES, record.readUnsignedByte());
-
- position.setCourse(record.readUnsignedShortLE());
- position.setAltitude(record.readShortLE());
-
- } else {
-
- if (sensorType == 3) {
- continue;
- }
-
- decodeSensor(position, record, sensorType, sensorId);
+ } else {
+ if (sensorType == 3) {
+ continue;
}
- }
-
- positions.add(position);
- } else if (type == 3) {
+ decodeSensor(position, record, sensorType, sensorId);
- Position position = new Position(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
+ }
+ }
- getLastLocation(position, new Date(record.readUnsignedIntLE() * 1000));
+ positions.add(position);
- record.readUnsignedByte(); // function identifier
+ } else if (type == 3) {
- position.set(Position.KEY_EVENT, record.readUnsignedByte());
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
- positions.add(position);
+ getLastLocation(position, new Date(record.readUnsignedIntLE() * 1000));
- }
- }
+ record.readUnsignedByte(); // function identifier
- buf.readUnsignedByte(); // checksum
+ position.set(Position.KEY_EVENT, record.readUnsignedByte());
- MqttMessage response = MqttMessageBuilders.pubAck()
- .packetId(message.variableHeader().packetId())
- .build();
+ positions.add(position);
- if (channel != null) {
- channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
-
- return positions.isEmpty() ? null : positions;
-
}
- return null;
+ buf.readUnsignedByte(); // checksum
+
+ return positions.isEmpty() ? null : positions;
}
}
diff --git a/src/main/java/org/traccar/protocol/ItsProtocol.java b/src/main/java/org/traccar/protocol/ItsProtocol.java
index 5148e8ab0..7d59ea60c 100644
--- a/src/main/java/org/traccar/protocol/ItsProtocol.java
+++ b/src/main/java/org/traccar/protocol/ItsProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class ItsProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Ivt401Protocol.java b/src/main/java/org/traccar/protocol/Ivt401Protocol.java
index 763457641..5132c7467 100644
--- a/src/main/java/org/traccar/protocol/Ivt401Protocol.java
+++ b/src/main/java/org/traccar/protocol/Ivt401Protocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Ivt401Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/JidoProtocol.java b/src/main/java/org/traccar/protocol/JidoProtocol.java
index 78aa6c81c..b30cc586a 100644
--- a/src/main/java/org/traccar/protocol/JidoProtocol.java
+++ b/src/main/java/org/traccar/protocol/JidoProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class JidoProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/JpKorjarProtocol.java b/src/main/java/org/traccar/protocol/JpKorjarProtocol.java
index 30c8e9977..ae312ea3e 100644
--- a/src/main/java/org/traccar/protocol/JpKorjarProtocol.java
+++ b/src/main/java/org/traccar/protocol/JpKorjarProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class JpKorjarProtocol extends BaseProtocol {
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 bf0b3379e..9dc62662f 100644
--- a/src/main/java/org/traccar/protocol/Jt600Protocol.java
+++ b/src/main/java/org/traccar/protocol/Jt600Protocol.java
@@ -22,7 +22,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Jt600Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java
index 9ed44f565..eca7e2d11 100644
--- a/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java
@@ -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) {
@@ -105,15 +105,9 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
double longitude = convertCoordinate(BcdUtil.readInteger(buf, 9));
byte flags = buf.readByte();
- position.setValid((flags & 0x1) == 0x1);
- if ((flags & 0x2) == 0) {
- latitude = -latitude;
- }
- position.setLatitude(latitude);
- if ((flags & 0x4) == 0) {
- longitude = -longitude;
- }
- position.setLongitude(longitude);
+ position.setValid(BitUtil.check(flags, 0));
+ position.setLatitude(BitUtil.check(flags, 1) ? latitude : -latitude);
+ position.setLongitude(BitUtil.check(flags, 2) ? longitude : -longitude);
position.setSpeed(BcdUtil.readInteger(buf, 2));
position.setCourse(buf.readUnsignedByte() * 2.0);
@@ -123,9 +117,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);
@@ -386,7 +380,7 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
.expression("([AV]),") // validity
.number("(d+),") // speed
.number("(d+),") // course
- .number("d+,") // event source
+ .number("(d+),") // event source
.number("d+,") // unlock verification
.number("(d+),") // rfid
.number("d+,") // password verification
@@ -419,6 +413,8 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
position.setSpeed(UnitsConverter.knotsFromMph(parser.nextDouble()));
position.setCourse(parser.nextDouble());
+ position.set("eventSource", parser.nextInt());
+
String rfid = parser.next();
if (!rfid.equals("0000000000")) {
position.set(Position.KEY_DRIVER_UNIQUE_ID, rfid);
diff --git a/src/main/java/org/traccar/protocol/KenjiProtocol.java b/src/main/java/org/traccar/protocol/KenjiProtocol.java
index 8d78c8c56..b4e610cbd 100644
--- a/src/main/java/org/traccar/protocol/KenjiProtocol.java
+++ b/src/main/java/org/traccar/protocol/KenjiProtocol.java
@@ -24,7 +24,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class KenjiProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/KhdProtocol.java b/src/main/java/org/traccar/protocol/KhdProtocol.java
index 521274de5..add13ef16 100644
--- a/src/main/java/org/traccar/protocol/KhdProtocol.java
+++ b/src/main/java/org/traccar/protocol/KhdProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class KhdProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java b/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java
index d7c236c4f..e88b34478 100644
--- a/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java
@@ -20,6 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
+import org.traccar.helper.BufferUtil;
import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
@@ -169,7 +170,9 @@ public class KhdProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_FUEL_LEVEL, BitUtil.from(odometer, 16));
}
- position.set(Position.KEY_STATUS, buf.readUnsignedInt());
+ long status = buf.readUnsignedInt();
+ position.set(Position.KEY_IGNITION, BitUtil.check(status, 7 + 3 * 8));
+ position.set(Position.KEY_STATUS, status);
buf.readUnsignedShort();
buf.readUnsignedByte();
@@ -185,8 +188,7 @@ public class KhdProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedShort(); // data length
int dataType = buf.readUnsignedByte();
-
- buf.readUnsignedByte(); // content length
+ int dataLength = buf.readUnsignedByte();
switch (dataType) {
case 0x01:
@@ -197,6 +199,20 @@ public class KhdProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.PREFIX_TEMP + 1,
buf.readUnsignedByte() * 100 + buf.readUnsignedByte());
break;
+ case 0x05:
+ int sign = buf.readUnsignedByte();
+ switch (sign) {
+ case 1:
+ position.set("sign", true);
+ break;
+ case 2:
+ position.set("sign", false);
+ break;
+ default:
+ break;
+ }
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, BufferUtil.readString(buf, dataLength - 1));
+ break;
case 0x18:
for (int i = 1; i <= 4; i++) {
double value = buf.readUnsignedShort();
@@ -205,6 +221,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/L100Protocol.java b/src/main/java/org/traccar/protocol/L100Protocol.java
index 0edea6095..fa6d1b07e 100644
--- a/src/main/java/org/traccar/protocol/L100Protocol.java
+++ b/src/main/java/org/traccar/protocol/L100Protocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class L100Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/LacakProtocol.java b/src/main/java/org/traccar/protocol/LacakProtocol.java
index bbebd51ed..ddaf5078d 100644
--- a/src/main/java/org/traccar/protocol/LacakProtocol.java
+++ b/src/main/java/org/traccar/protocol/LacakProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class LacakProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/LacakProtocolDecoder.java b/src/main/java/org/traccar/protocol/LacakProtocolDecoder.java
index 809fafc90..66aab3490 100644
--- a/src/main/java/org/traccar/protocol/LacakProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/LacakProtocolDecoder.java
@@ -24,8 +24,8 @@ 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 249d3bcbe..65b1a57e9 100644
--- a/src/main/java/org/traccar/protocol/LaipacProtocol.java
+++ b/src/main/java/org/traccar/protocol/LaipacProtocol.java
@@ -24,7 +24,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class LaipacProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java b/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java
index e9570ee11..343d42e09 100644
--- a/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java
@@ -28,6 +28,7 @@ import org.traccar.helper.PatternBuilder;
import org.traccar.model.CellTower;
import org.traccar.model.Network;
import org.traccar.model.Position;
+import org.traccar.helper.BitUtil;
import java.net.SocketAddress;
import java.util.regex.Pattern;
@@ -108,19 +109,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 +145,9 @@ public class LaipacProtocolDecoder extends BaseProtocolDecoder {
case "3":
responseCode = "d";
break;
+ case "M":
+ responseCode = "m";
+ break;
case "S":
case "T":
responseCode = "t";
@@ -209,6 +225,8 @@ public class LaipacProtocolDecoder extends BaseProtocolDecoder {
return null;
}
+ String model = getDeviceModel(deviceSession);
+
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
@@ -230,12 +248,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();
diff --git a/src/main/java/org/traccar/protocol/LeafSpyProtocol.java b/src/main/java/org/traccar/protocol/LeafSpyProtocol.java
index 7e13e23d0..9e167e7ba 100644
--- a/src/main/java/org/traccar/protocol/LeafSpyProtocol.java
+++ b/src/main/java/org/traccar/protocol/LeafSpyProtocol.java
@@ -24,7 +24,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class LeafSpyProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/M2cProtocol.java b/src/main/java/org/traccar/protocol/M2cProtocol.java
index a23ea0f57..8abc30f60 100644
--- a/src/main/java/org/traccar/protocol/M2cProtocol.java
+++ b/src/main/java/org/traccar/protocol/M2cProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class M2cProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/M2mProtocol.java b/src/main/java/org/traccar/protocol/M2mProtocol.java
index 6809d800c..03a069d66 100644
--- a/src/main/java/org/traccar/protocol/M2mProtocol.java
+++ b/src/main/java/org/traccar/protocol/M2mProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class M2mProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/MaestroProtocol.java b/src/main/java/org/traccar/protocol/MaestroProtocol.java
index 38a67f9a4..29f0b8897 100644
--- a/src/main/java/org/traccar/protocol/MaestroProtocol.java
+++ b/src/main/java/org/traccar/protocol/MaestroProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class MaestroProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/ManPowerProtocol.java b/src/main/java/org/traccar/protocol/ManPowerProtocol.java
index 492e86605..ba2414ca7 100644
--- a/src/main/java/org/traccar/protocol/ManPowerProtocol.java
+++ b/src/main/java/org/traccar/protocol/ManPowerProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class ManPowerProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Mavlink2Protocol.java b/src/main/java/org/traccar/protocol/Mavlink2Protocol.java
index cf65a2db3..916fb7467 100644
--- a/src/main/java/org/traccar/protocol/Mavlink2Protocol.java
+++ b/src/main/java/org/traccar/protocol/Mavlink2Protocol.java
@@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Mavlink2Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/MegastekProtocol.java b/src/main/java/org/traccar/protocol/MegastekProtocol.java
index 10215eb7c..9f8937f01 100644
--- a/src/main/java/org/traccar/protocol/MegastekProtocol.java
+++ b/src/main/java/org/traccar/protocol/MegastekProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class MegastekProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/MeiligaoProtocol.java b/src/main/java/org/traccar/protocol/MeiligaoProtocol.java
index 492094ce3..d86a00fb3 100644
--- a/src/main/java/org/traccar/protocol/MeiligaoProtocol.java
+++ b/src/main/java/org/traccar/protocol/MeiligaoProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class MeiligaoProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java
index f3b56973a..1f8c4d2da 100644
--- a/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java
@@ -282,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());
diff --git a/src/main/java/org/traccar/protocol/MeiligaoProtocolEncoder.java b/src/main/java/org/traccar/protocol/MeiligaoProtocolEncoder.java
index 5859d91ce..6d3b4f7e9 100644
--- a/src/main/java/org/traccar/protocol/MeiligaoProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/MeiligaoProtocolEncoder.java
@@ -24,7 +24,6 @@ import org.traccar.helper.Checksum;
import org.traccar.helper.DataConverter;
import org.traccar.helper.model.AttributeUtil;
import org.traccar.model.Command;
-import org.traccar.model.Device;
import java.nio.charset.StandardCharsets;
import java.util.Set;
@@ -64,7 +63,7 @@ public class MeiligaoProtocolEncoder extends BaseProtocolEncoder {
int outputCount;
int outputType;
- String model = getCacheManager().getObject(Device.class, deviceId).getModel();
+ String model = getDeviceModel(deviceId);
if (model != null && Set.of("TK510", "GT08", "TK208", "TK228", "MT05").contains(model)) {
outputCount = 5;
diff --git a/src/main/java/org/traccar/protocol/MeitrackProtocol.java b/src/main/java/org/traccar/protocol/MeitrackProtocol.java
index c6eba8fe1..4109b22c9 100644
--- a/src/main/java/org/traccar/protocol/MeitrackProtocol.java
+++ b/src/main/java/org/traccar/protocol/MeitrackProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class MeitrackProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java
index 343141dca..88b6380a5 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 - 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.
@@ -16,10 +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.model.Device;
import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
@@ -30,12 +30,14 @@ import org.traccar.helper.UnitsConverter;
import org.traccar.model.CellTower;
import org.traccar.model.Network;
import org.traccar.model.Position;
+import org.traccar.model.WifiAccessPoint;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
+import java.util.Objects;
import java.util.regex.Pattern;
public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
@@ -204,11 +206,7 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.PREFIX_ADC + i, parser.nextHexInt());
}
- String model = getCacheManager().getObject(Device.class, deviceSession.getDeviceId()).getModel();
- if (model == null) {
- model = "";
- }
- switch (model.toUpperCase()) {
+ switch (Objects.requireNonNullElse(getDeviceModel(deviceSession), "").toUpperCase()) {
case "MVT340":
case "MVT380":
position.set(Position.KEY_BATTERY, parser.nextHexInt() * 3.0 * 2.0 / 1024.0);
@@ -394,6 +392,8 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
+ Network network = new Network();
+
buf.readUnsignedShortLE(); // length
buf.readUnsignedShortLE(); // index
@@ -420,6 +420,12 @@ 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;
@@ -455,12 +461,18 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
case 0x16:
position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShortLE() * 0.01);
break;
+ case 0x17:
+ position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShortLE() * 0.01);
+ 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 0x29:
+ position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedShortLE() * 0.01);
+ break;
case 0x40:
position.set(Position.KEY_EVENT, buf.readUnsignedShortLE());
break;
@@ -504,18 +516,26 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
position.setTime(new Date((946684800 + buf.readUnsignedIntLE()) * 1000)); // 2000-01-01
break;
case 0x0C:
- case 0x9B:
position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
break;
case 0x0D:
position.set("runtime", buf.readUnsignedIntLE());
break;
+ case 0x25:
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, String.valueOf(buf.readUnsignedIntLE()));
+ break;
+ case 0x9B:
+ position.set(Position.KEY_OBD_ODOMETER, buf.readUnsignedIntLE());
+ break;
case 0xA0:
position.set(Position.KEY_FUEL_USED, buf.readUnsignedIntLE() * 0.001);
break;
case 0xA2:
position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedIntLE() * 0.01);
break;
+ case 0xFEF4:
+ position.set(Position.KEY_HOURS, buf.readUnsignedIntLE() * 60000);
+ break;
default:
buf.readUnsignedIntLE();
break;
@@ -528,6 +548,28 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
int id = extension ? buf.readUnsignedShort() : buf.readUnsignedByte();
int length = buf.readUnsignedByte();
switch (id) {
+ case 0x1D:
+ case 0x1E:
+ case 0x1F:
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ String wifiMac = ByteBufUtil.hexDump(buf.readSlice(6)).replaceAll("(..)", "$1:");
+ network.addWifiAccessPoint(WifiAccessPoint.from(
+ wifiMac.substring(0, wifiMac.length() - 1), buf.readShortLE()));
+ break;
+ case 0x0E:
+ case 0x0F:
+ case 0x10:
+ case 0x12:
+ case 0x13:
+ network.addCellTower(CellTower.from(
+ buf.readUnsignedShortLE(), buf.readUnsignedShortLE(),
+ buf.readUnsignedShortLE(), buf.readUnsignedIntLE(), buf.readShortLE()));
+ break;
case 0x2A:
case 0x2B:
case 0x2C:
@@ -539,17 +581,48 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedByte(); // label
position.set(Position.PREFIX_TEMP + (id - 0x2A), buf.readShortLE() * 0.01);
break;
+ case 0x4B:
+ buf.skipBytes(length); // network information
+ break;
case 0xFE31:
buf.readUnsignedByte(); // alarm protocol
buf.readUnsignedByte(); // alarm type
buf.skipBytes(length - 2);
break;
+ case 0xFE73:
+ buf.readUnsignedByte(); // version
+ position.set(
+ "tagName",
+ buf.readCharSequence(buf.readUnsignedByte(), StandardCharsets.US_ASCII).toString());
+ buf.skipBytes(6); // mac
+ position.set("tagBattery", buf.readUnsignedByte());
+ position.set("tagTemp", buf.readUnsignedShortLE() / 256.0);
+ position.set("tagHumidity", buf.readUnsignedShortLE() / 256.0);
+ buf.readUnsignedShortLE(); // high temperature threshold
+ buf.readUnsignedShortLE(); // low temperature threshold
+ buf.readUnsignedShortLE(); // high humidity threshold
+ buf.readUnsignedShortLE(); // low humidity threshold
+ 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;
}
}
+ if (network.getCellTowers() != null || network.getWifiAccessPoints() != null) {
+ position.setNetwork(network);
+ }
positions.add(position);
}
@@ -624,6 +697,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/MictrackProtocol.java b/src/main/java/org/traccar/protocol/MictrackProtocol.java
index ccbc4db4c..08bbe0c82 100644
--- a/src/main/java/org/traccar/protocol/MictrackProtocol.java
+++ b/src/main/java/org/traccar/protocol/MictrackProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class MictrackProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/MilesmateProtocol.java b/src/main/java/org/traccar/protocol/MilesmateProtocol.java
index 59212e791..607dfc5bf 100644
--- a/src/main/java/org/traccar/protocol/MilesmateProtocol.java
+++ b/src/main/java/org/traccar/protocol/MilesmateProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class MilesmateProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/MiniFinderProtocol.java b/src/main/java/org/traccar/protocol/MiniFinderProtocol.java
index 44599accc..1cb2a0007 100644
--- a/src/main/java/org/traccar/protocol/MiniFinderProtocol.java
+++ b/src/main/java/org/traccar/protocol/MiniFinderProtocol.java
@@ -24,7 +24,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class MiniFinderProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/MiniFinderProtocolDecoder.java b/src/main/java/org/traccar/protocol/MiniFinderProtocolDecoder.java
index f2e5eb905..1fdb1ece0 100644
--- a/src/main/java/org/traccar/protocol/MiniFinderProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MiniFinderProtocolDecoder.java
@@ -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 5499a274e..082b9146d 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.
@@ -20,19 +20,24 @@ 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 javax.inject.Inject;
+import jakarta.inject.Inject;
public class Minifinder2Protocol extends BaseProtocol {
@Inject
public Minifinder2Protocol(Config config) {
+ setSupportedDataCommands(
+ Command.TYPE_FIRMWARE_UPDATE,
+ Command.TYPE_CONFIGURATION);
addServer(new TrackerServer(config, getName(), false) {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
- pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1200, 2, 2, 4, 0, true));
+ 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 0b08badb8..64373e344 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 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2024 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
+import org.traccar.helper.BufferUtil;
import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
@@ -48,6 +49,8 @@ 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(long code) {
@@ -146,14 +149,13 @@ 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<>();
- boolean hasLocation = false;
Position position = new Position(getProtocolName());
DeviceSession deviceSession = null;
@@ -163,12 +165,8 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
int key = buf.readUnsignedByte();
if (keys.contains(key)) {
- if (!hasLocation) {
- getLastLocation(position, null);
- }
positions.add(position);
keys.clear();
- hasLocation = false;
position = new Position(getProtocolName());
}
keys.add(key);
@@ -177,8 +175,9 @@ 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:
long alarm = buf.readUnsignedIntLE();
@@ -192,7 +191,6 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() * 0.001);
break;
case 0x20:
- hasLocation = true;
position.setLatitude(buf.readIntLE() * 0.0000001);
position.setLongitude(buf.readIntLE() * 0.0000001);
position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShortLE()));
@@ -232,11 +230,18 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
position.setLatitude(buf.readIntLE() * 0.0000001);
position.setLongitude(buf.readIntLE() * 0.0000001);
position.setValid(true);
- hasLocation = true;
break;
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;
@@ -249,7 +254,6 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
position.setLatitude(buf.readIntLE() * 0.0000001);
position.setLongitude(buf.readIntLE() * 0.0000001);
position.setValid(true);
- hasLocation = true;
}
if (BitUtil.check(beaconFlags, 6)) {
position.set("description", buf.readCharSequence(
@@ -263,7 +267,6 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
position.setLatitude(buf.readIntLE() * 0.0000001);
position.setLongitude(buf.readIntLE() * 0.0000001);
position.setValid(true);
- hasLocation = true;
break;
case 0x30:
buf.readUnsignedIntLE(); // timestamp
@@ -277,6 +280,14 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
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
int heartRate = buf.readUnsignedByte();
@@ -290,14 +301,14 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
buf.readerIndex(endIndex);
}
- if (!hasLocation) {
- getLastLocation(position, null);
- }
positions.add(position);
if (deviceSession != null) {
for (Position p : positions) {
p.setDeviceId(deviceSession.getDeviceId());
+ if (!p.getValid() && !p.hasAttribute(Position.KEY_HDOP)) {
+ getLastLocation(p, null);
+ }
}
} else {
return null;
@@ -305,9 +316,195 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
return positions;
+ } else if (type == MSG_CONFIGURATION) {
+
+ return decodeConfiguration(channel, remoteAddress, buf);
+
+ } 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;
}
+ private Position decodeConfiguration(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ while (buf.isReadable()) {
+ int length = buf.readUnsignedByte() - 1;
+ int endIndex = buf.readerIndex() + length + 1;
+ int key = buf.readUnsignedByte();
+
+ switch (key) {
+ case 0x01:
+ position.set("moduleNumber", buf.readUnsignedInt());
+ break;
+ case 0x02:
+ position.set(Position.KEY_VERSION_FW, String.valueOf(buf.readUnsignedInt()));
+ break;
+ case 0x03:
+ position.set("imei", buf.readCharSequence(length, StandardCharsets.US_ASCII).toString());
+ break;
+ case 0x04:
+ position.set(Position.KEY_ICCID, BufferUtil.readString(buf, length));
+ break;
+ case 0x05:
+ position.set("bleMac", ByteBufUtil.hexDump(buf.readSlice(length)));
+ break;
+ case 0x06:
+ position.set("settingTime", buf.readUnsignedInt());
+ break;
+ case 0x07:
+ position.set("runTimes", buf.readUnsignedInt());
+ break;
+ case 0x0A:
+ position.set("interval", buf.readUnsignedMedium());
+ position.set("petMode", buf.readUnsignedByte());
+ break;
+ case 0x0D:
+ position.set("passwordProtect", buf.readUnsignedInt());
+ break;
+ case 0x0E:
+ position.set("timeZone", (int) buf.readByte());
+ break;
+ case 0x0F:
+ position.set("enableControl", buf.readUnsignedInt());
+ break;
+ case 0x13:
+ position.set("deviceName", BufferUtil.readString(buf, length));
+ break;
+ case 0x14:
+ position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001);
+ break;
+ case 0x15:
+ position.set("bleLatitude", buf.readIntLE() * 0.0000001);
+ position.set("bleLongitude", buf.readIntLE() * 0.0000001);
+ position.set("bleLocation", BufferUtil.readString(buf, length - 8));
+ break;
+ case 0x17:
+ position.set("gpsUrl", BufferUtil.readString(buf, length));
+ break;
+ case 0x18:
+ position.set("lbsUrl", BufferUtil.readString(buf, length));
+ break;
+ case 0x1A:
+ position.set("firmware", BufferUtil.readString(buf, length));
+ break;
+ case 0x1B:
+ position.set("gsmModule", BufferUtil.readString(buf, length));
+ break;
+ case 0x1D:
+ position.set("agpsUpdate", buf.readUnsignedByte());
+ position.set("agpsLatitude", buf.readIntLE() * 0.0000001);
+ position.set("agpsLongitude", buf.readIntLE() * 0.0000001);
+ break;
+ case 0x30:
+ position.set("numberFlag", buf.readUnsignedByte());
+ position.set("number", BufferUtil.readString(buf, length - 1));
+ break;
+ case 0x31:
+ position.set("prefixFlag", buf.readUnsignedByte());
+ position.set("prefix", BufferUtil.readString(buf, length - 1));
+ break;
+ case 0x33:
+ position.set("phoneSwitches", buf.readUnsignedByte());
+ break;
+ case 0x40:
+ position.set("apn", BufferUtil.readString(buf, length));
+ break;
+ case 0x41:
+ position.set("apnUser", BufferUtil.readString(buf, length));
+ break;
+ case 0x42:
+ position.set("apnPassword", BufferUtil.readString(buf, length));
+ break;
+ case 0x43:
+ buf.readUnsignedByte(); // flag
+ position.set("port", buf.readUnsignedShort());
+ position.set("server", BufferUtil.readString(buf, length - 3));
+ break;
+ case 0x44:
+ position.set("heartbeatInterval", buf.readUnsignedInt());
+ position.set("uploadInterval", buf.readUnsignedInt());
+ position.set("uploadLazyInterval", buf.readUnsignedInt());
+ break;
+ case 0x47:
+ position.set("deviceId", BufferUtil.readString(buf, length));
+ break;
+ case 0x4E:
+ position.set("gsmBand", buf.readUnsignedByte());
+ break;
+ case 0x50:
+ position.set("powerAlert", buf.readUnsignedInt());
+ break;
+ case 0x51:
+ position.set("geoAlert", buf.readUnsignedInt());
+ break;
+ case 0x53:
+ position.set("motionAlert", buf.readUnsignedInt());
+ break;
+ case 0x5C:
+ position.set("barkLevel", buf.readUnsignedByte());
+ position.set("barkInterval", buf.readUnsignedInt());
+ break;
+ case 0x61:
+ position.set("msisdn", BufferUtil.readString(buf, length));
+ break;
+ case 0x62:
+ position.set("wifiWhitelist", buf.readUnsignedByte());
+ position.set("wifiWhitelistMac", ByteBufUtil.hexDump(buf.readSlice(6)));
+ break;
+ case 0x64:
+ position.set(Position.KEY_RSSI, buf.readUnsignedByte());
+ position.set("networkBand", buf.readUnsignedInt());
+ position.set(Position.KEY_OPERATOR, BufferUtil.readString(buf, length - 5));
+ break;
+ case 0x65:
+ position.set(Position.KEY_RSSI, buf.readUnsignedByte());
+ position.set("networkStatus", buf.readUnsignedByte());
+ position.set("serverStatus", buf.readUnsignedByte());
+ position.set("networkPlmn", ByteBufUtil.hexDump(buf.readSlice(6)));
+ position.set("homePlmn", ByteBufUtil.hexDump(buf.readSlice(6)));
+ break;
+ case 0x66:
+ position.set("imsi", BufferUtil.readString(buf, length));
+ break;
+ case 0x75:
+ position.set("extraEnableControl", buf.readUnsignedInt());
+ break;
+ default:
+ break;
+ }
+
+ buf.readerIndex(endIndex);
+ }
+
+ return position;
+ }
+
}
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..6e330a4dd
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Minifinder2ProtocolEncoder.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.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 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) {
+
+ if (command.getType().equals(Command.TYPE_CONFIGURATION)) {
+ ByteBuf content = Unpooled.buffer();
+ content.writeByte(Minifinder2ProtocolDecoder.MSG_CONFIGURATION);
+ content.writeByte(1); // length
+ content.writeByte(0xF0); // type
+ }
+
+ if ("Nano".equalsIgnoreCase(getDeviceModel(command.getDeviceId()))) {
+ 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 1b06c2249..36d6b5ed2 100644
--- a/src/main/java/org/traccar/protocol/MobilogixProtocol.java
+++ b/src/main/java/org/traccar/protocol/MobilogixProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class MobilogixProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/MoovboxProtocol.java b/src/main/java/org/traccar/protocol/MoovboxProtocol.java
index 16438e122..af853fe67 100644
--- a/src/main/java/org/traccar/protocol/MoovboxProtocol.java
+++ b/src/main/java/org/traccar/protocol/MoovboxProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class MoovboxProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/MotorProtocol.java b/src/main/java/org/traccar/protocol/MotorProtocol.java
index 3101c9b75..f17886577 100644
--- a/src/main/java/org/traccar/protocol/MotorProtocol.java
+++ b/src/main/java/org/traccar/protocol/MotorProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class MotorProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Mta6Protocol.java b/src/main/java/org/traccar/protocol/Mta6Protocol.java
index 019fe4fa9..c1c6eb829 100644
--- a/src/main/java/org/traccar/protocol/Mta6Protocol.java
+++ b/src/main/java/org/traccar/protocol/Mta6Protocol.java
@@ -24,7 +24,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.config.Keys;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Mta6Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Mta6ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Mta6ProtocolDecoder.java
index 896c7a2d2..9704cf099 100644
--- a/src/main/java/org/traccar/protocol/Mta6ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Mta6ProtocolDecoder.java
@@ -96,7 +96,7 @@ public class Mta6ProtocolDecoder extends BaseProtocolDecoder {
}
- private static class TimeReader extends FloatReader {
+ private static final class TimeReader extends FloatReader {
private long weekNumber;
diff --git a/src/main/java/org/traccar/protocol/MtxProtocol.java b/src/main/java/org/traccar/protocol/MtxProtocol.java
index e085b6221..12d324019 100644
--- a/src/main/java/org/traccar/protocol/MtxProtocol.java
+++ b/src/main/java/org/traccar/protocol/MtxProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class MtxProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/MxtProtocol.java b/src/main/java/org/traccar/protocol/MxtProtocol.java
index 1190bf527..2f0cc1658 100644
--- a/src/main/java/org/traccar/protocol/MxtProtocol.java
+++ b/src/main/java/org/traccar/protocol/MxtProtocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class MxtProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/NavigilProtocol.java b/src/main/java/org/traccar/protocol/NavigilProtocol.java
index 46a6c33a5..a309235c5 100644
--- a/src/main/java/org/traccar/protocol/NavigilProtocol.java
+++ b/src/main/java/org/traccar/protocol/NavigilProtocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class NavigilProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/NavisProtocol.java b/src/main/java/org/traccar/protocol/NavisProtocol.java
index 640a77803..96b5b0de0 100644
--- a/src/main/java/org/traccar/protocol/NavisProtocol.java
+++ b/src/main/java/org/traccar/protocol/NavisProtocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class NavisProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/NavisetProtocol.java b/src/main/java/org/traccar/protocol/NavisetProtocol.java
index 388f141f8..6df0b0436 100644
--- a/src/main/java/org/traccar/protocol/NavisetProtocol.java
+++ b/src/main/java/org/traccar/protocol/NavisetProtocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class NavisetProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/NavtelecomProtocol.java b/src/main/java/org/traccar/protocol/NavtelecomProtocol.java
index 50013d1a4..de5f93df1 100644
--- a/src/main/java/org/traccar/protocol/NavtelecomProtocol.java
+++ b/src/main/java/org/traccar/protocol/NavtelecomProtocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class NavtelecomProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java b/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java
index 08b1a8d0f..cd7ffa0e1 100644
--- a/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java
@@ -193,12 +193,12 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- for (int j = 0; j < bits.length(); j++) {
- if (bits.get(j)) {
+ for (int j = 1; j <= bits.length(); j++) {
+ if (bits.get(j - 1)) {
int value;
- switch (j + 1) {
+ switch (j) {
case 1:
position.set(Position.KEY_INDEX, buf.readUnsignedIntLE());
break;
@@ -208,6 +208,18 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder {
case 3:
position.setDeviceTime(new Date(buf.readUnsignedIntLE() * 1000));
break;
+ case 4:
+ value = buf.readUnsignedByte();
+ position.set(
+ Position.KEY_ALARM,
+ BitUtil.check(value, 2) ? Position.ALARM_GENERAL : null);
+ int guardMode = BitUtil.between(value, 3, 4);
+ position.set(Position.KEY_ARMED, (0 < guardMode) && (guardMode < 3));
+ break;
+ case 5:
+ value = buf.readUnsignedByte();
+ position.set(Position.KEY_ROAMING, BitUtil.check(value, 6) ? true : null);
+ break;
case 8:
value = buf.readUnsignedByte();
position.setValid(BitUtil.check(value, 1));
@@ -232,7 +244,7 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder {
position.setCourse(buf.readUnsignedShortLE());
break;
case 15:
- position.set(Position.KEY_ODOMETER, buf.readFloatLE());
+ position.set(Position.KEY_ODOMETER, buf.readFloatLE() * 1000);
break;
case 19:
position.set(Position.KEY_POWER, buf.readShortLE() * 0.001);
@@ -246,7 +258,7 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder {
case 24:
case 25:
case 26:
- position.set(Position.PREFIX_ADC + (j + 2 - 21), buf.readUnsignedShortLE() * 0.001);
+ position.set(Position.PREFIX_ADC + (j + 1 - 21), buf.readUnsignedShortLE() * 0.001);
break;
case 29:
value = buf.readUnsignedByte();
@@ -262,14 +274,14 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder {
break;
case 33:
case 34:
- position.set(Position.PREFIX_COUNT + (j + 2 - 33), buf.readUnsignedIntLE());
+ position.set(Position.PREFIX_COUNT + (j + 1 - 33), buf.readUnsignedIntLE());
break;
case 35:
case 36:
- position.set("freq" + (j + 2 - 35), buf.readUnsignedShortLE());
+ position.set("freq" + (j + 1 - 35), buf.readUnsignedShortLE());
break;
case 37:
- position.set(Position.KEY_HOURS, buf.readUnsignedIntLE());
+ position.set(Position.KEY_HOURS, buf.readUnsignedIntLE() * 1000);
break;
case 38:
case 39:
@@ -278,7 +290,8 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder {
case 42:
case 43:
value = buf.readUnsignedShortLE();
- position.set("fuel" + (j + 2 - 38), (value < 65500) ? value : null);
+ position.set(
+ Position.KEY_FUEL_LEVEL + (j + 1 - 38), (value < 65500) ? value : null);
break;
case 44:
value = buf.readUnsignedShortLE();
@@ -294,8 +307,75 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder {
case 52:
value = buf.readByte();
position.set(
- Position.PREFIX_TEMP + (j + 2 - 45),
- (value != (byte) 0x80) ? value : null);
+ Position.PREFIX_TEMP + (j + 1 - 45), (value != (byte) 0x80) ? value : null);
+ break;
+ case 53:
+ value = buf.readUnsignedShortLE();
+ if (value != 0x7FFF) {
+ if (BitUtil.check(value, 15)) {
+ position.set("obdFuelLevel", BitUtil.to(value, 14));
+ } else {
+ position.set("obdFuel", BitUtil.to(value, 14) * 0.1);
+ }
+ }
+ break;
+ case 54:
+ double fuelUsed = buf.readFloatLE() * 0.5;
+ position.set(Position.KEY_FUEL_USED, (fuelUsed >= 0) ? fuelUsed : null);
+ break;
+ case 55:
+ value = buf.readUnsignedShortLE();
+ position.set(Position.KEY_RPM, (value != 0xFFFF) ? value : null);
+ break;
+ case 56:
+ value = buf.readByte();
+ position.set(Position.KEY_COOLANT_TEMP, (value != (byte) 0x80) ? value : null);
+ break;
+ case 57:
+ position.set(Position.KEY_OBD_ODOMETER, buf.readFloatLE() * 1000);
+ break;
+ case 58:
+ case 59:
+ case 60:
+ case 61:
+ case 62:
+ value = buf.readUnsignedShortLE();
+ position.set("axleWeight" + (j + 1 - 58), (value != 0xFFFF) ? value : null);
+ break;
+ case 63:
+ value = buf.readUnsignedByte();
+ position.set("acceleratorPosition", (value != 0xFF) ? value : null);
+ break;
+ case 64:
+ value = buf.readUnsignedByte();
+ position.set("brakePosition", (value != 0xFF) ? value : null);
+ break;
+ case 65:
+ value = buf.readUnsignedByte();
+ position.set(Position.KEY_ENGINE_LOAD, (value != 0xFF) ? value : null);
+ break;
+ case 66:
+ value = buf.readUnsignedShortLE();
+ if (value != 0x7FFF) {
+ if (BitUtil.check(value, 15)) {
+ position.set("obdAdBlueLevel", BitUtil.to(value, 14));
+ } else {
+ position.set("obdAdBlue", BitUtil.to(value, 14) * 0.1);
+ }
+ }
+ break;
+ case 67:
+ position.set("obdHours", buf.readUnsignedIntLE() * 1000);
+ break;
+ case 68:
+ value = buf.readUnsignedShortLE();
+ position.set(
+ Position.KEY_ODOMETER_SERVICE,
+ (value != 0xFFFF) ? (value * 5000) : null);
+ break;
+ case 69:
+ value = buf.readUnsignedByte();
+ position.set(Position.KEY_OBD_SPEED, (value != 0xFF) ? value : null);
break;
case 78:
case 79:
@@ -303,10 +383,39 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder {
case 81:
case 82:
case 83:
- position.set("fuelTemp" + (j + 2 - 78), (int) buf.readByte());
+ position.set("fuelTemp" + (j + 1 - 78), (int) buf.readByte());
+ break;
+ case 163:
+ case 164:
+ case 165:
+ case 166:
+ value = buf.readShortLE();
+ position.set(
+ Position.PREFIX_TEMP + (j + 1 + 8 - 163),
+ (value != (short) 0x8000) ? value * 0.05 : null);
+ break;
+ case 167:
+ case 168:
+ case 169:
+ case 170:
+ value = buf.readUnsignedByte();
+ position.set("humidity" + (j + 1 - 167), (value != 0xFF) ? value * 0.5 : null);
+ break;
+ case 206:
+ position.set("diagnostic", buf.readUnsignedIntLE());
break;
default:
- buf.skipBytes(getItemLength(j + 1));
+ if ((207 <= j) && (j <= 222)) {
+ position.set("user1Byte" + (j + 1 - 207), buf.readUnsignedByte());
+ } else if ((223 <= j) && (j <= 237)) {
+ position.set("user2Byte" + (j + 1 - 223), buf.readUnsignedShortLE());
+ } else if ((238 <= j) && (j <= 252)) {
+ position.set("user4Byte" + (j + 1 - 238), buf.readUnsignedIntLE());
+ } else if ((253 <= j) && (j <= 255)) {
+ position.set("user8Byte" + (j + 1 - 253), buf.readLongLE());
+ } else {
+ buf.skipBytes(getItemLength(j));
+ }
break;
}
}
diff --git a/src/main/java/org/traccar/protocol/NdtpV6Protocol.java b/src/main/java/org/traccar/protocol/NdtpV6Protocol.java
index ce0dbbef2..9493132f5 100644
--- a/src/main/java/org/traccar/protocol/NdtpV6Protocol.java
+++ b/src/main/java/org/traccar/protocol/NdtpV6Protocol.java
@@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class NdtpV6Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/NeosProtocol.java b/src/main/java/org/traccar/protocol/NeosProtocol.java
index 0787b6562..16a6ba5a0 100644
--- a/src/main/java/org/traccar/protocol/NeosProtocol.java
+++ b/src/main/java/org/traccar/protocol/NeosProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class NeosProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/NetProtocol.java b/src/main/java/org/traccar/protocol/NetProtocol.java
index f27e4afb8..e011660da 100644
--- a/src/main/java/org/traccar/protocol/NetProtocol.java
+++ b/src/main/java/org/traccar/protocol/NetProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class NetProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/NiotProtocol.java b/src/main/java/org/traccar/protocol/NiotProtocol.java
index 0fbe0c689..7eacd5ff3 100644
--- a/src/main/java/org/traccar/protocol/NiotProtocol.java
+++ b/src/main/java/org/traccar/protocol/NiotProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class NiotProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/NoranProtocol.java b/src/main/java/org/traccar/protocol/NoranProtocol.java
index 626991029..d03e52be5 100644
--- a/src/main/java/org/traccar/protocol/NoranProtocol.java
+++ b/src/main/java/org/traccar/protocol/NoranProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class NoranProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/NtoProtocol.java b/src/main/java/org/traccar/protocol/NtoProtocol.java
new file mode 100644
index 000000000..d3596e287
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NtoProtocol.java
@@ -0,0 +1,42 @@
+/*
+ * 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.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+import jakarta.inject.Inject;
+import org.traccar.BaseProtocol;
+import org.traccar.CharacterDelimiterFrameDecoder;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+public class NtoProtocol extends BaseProtocol {
+
+ @Inject
+ public NtoProtocol(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 NtoProtocolDecoder(NtoProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/NtoProtocolDecoder.java b/src/main/java/org/traccar/protocol/NtoProtocolDecoder.java
new file mode 100644
index 000000000..ba9ebd95d
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NtoProtocolDecoder.java
@@ -0,0 +1,92 @@
+/*
+ * 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 org.traccar.BaseProtocolDecoder;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+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.util.regex.Pattern;
+
+public class NtoProtocolDecoder extends BaseProtocolDecoder {
+
+ public NtoProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("^NB,") // manufacturer
+ .number("(d+),") // imei
+ .expression("(...),") // type
+ .number("(dd)(dd)(dd),") // date (ddmmyy)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .expression("([AVM]),") // validity
+ .number("([NS]),(dd)(dd.d+),") // latitude
+ .number("([EW]),(ddd)(dd.d+),") // longitude
+ .number("(d+.?d*),") // speed
+ .number("(d+),") // course
+ .number("(x+),") // status
+ .any()
+ .compile();
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ Parser parser = new Parser(PATTERN, (String) msg);
+ 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_TYPE, parser.next());
+
+ position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+
+ position.setValid(parser.next().equals("A"));
+ position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN));
+ position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN));
+ position.setSpeed(parser.nextDouble());
+ position.setCourse(parser.nextInt());
+
+ long status = parser.nextHexLong();
+ position.set(Position.KEY_STATUS, status);
+ position.set(Position.KEY_ALARM, BitUtil.check(status, 1) ? Position.ALARM_JAMMING : null);
+ position.set(Position.KEY_ALARM, BitUtil.check(status, 3 * 8 + 1) ? Position.ALARM_POWER_CUT : null);
+ position.set(Position.KEY_ALARM, BitUtil.check(status, 3 * 8 + 2) ? Position.ALARM_OVERSPEED : null);
+ position.set(Position.KEY_ALARM, BitUtil.check(status, 3 * 8 + 3) ? Position.ALARM_VIBRATION : null);
+ position.set(Position.KEY_ALARM, BitUtil.check(status, 3 * 8 + 4) ? Position.ALARM_GEOFENCE_ENTER : null);
+ position.set(Position.KEY_ALARM, BitUtil.check(status, 3 * 8 + 5) ? Position.ALARM_GEOFENCE_EXIT : null);
+ position.set(Position.KEY_ALARM, BitUtil.check(status, 4 * 8) ? Position.ALARM_LOW_BATTERY : null);
+ position.set(Position.KEY_ALARM, BitUtil.check(status, 4 * 8 + 4) ? Position.ALARM_DOOR : null);
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/NvsProtocol.java b/src/main/java/org/traccar/protocol/NvsProtocol.java
index 7ed488e38..8a4ece30d 100644
--- a/src/main/java/org/traccar/protocol/NvsProtocol.java
+++ b/src/main/java/org/traccar/protocol/NvsProtocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class NvsProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/NyitechProtocol.java b/src/main/java/org/traccar/protocol/NyitechProtocol.java
index e7ef10945..225b1bd5a 100644
--- a/src/main/java/org/traccar/protocol/NyitechProtocol.java
+++ b/src/main/java/org/traccar/protocol/NyitechProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.config.Config;
import java.nio.ByteOrder;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class NyitechProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/ObdDongleProtocol.java b/src/main/java/org/traccar/protocol/ObdDongleProtocol.java
index 94f450426..9fcc35d0d 100644
--- a/src/main/java/org/traccar/protocol/ObdDongleProtocol.java
+++ b/src/main/java/org/traccar/protocol/ObdDongleProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class ObdDongleProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/OigoProtocol.java b/src/main/java/org/traccar/protocol/OigoProtocol.java
index 0539bada6..3483f8270 100644
--- a/src/main/java/org/traccar/protocol/OigoProtocol.java
+++ b/src/main/java/org/traccar/protocol/OigoProtocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class OigoProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/OkoProtocol.java b/src/main/java/org/traccar/protocol/OkoProtocol.java
index 29c8bc1b9..6ca6c0e93 100644
--- a/src/main/java/org/traccar/protocol/OkoProtocol.java
+++ b/src/main/java/org/traccar/protocol/OkoProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class OkoProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/OmnicommProtocol.java b/src/main/java/org/traccar/protocol/OmnicommProtocol.java
index dd400c779..b59b84132 100644
--- a/src/main/java/org/traccar/protocol/OmnicommProtocol.java
+++ b/src/main/java/org/traccar/protocol/OmnicommProtocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class OmnicommProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/OpenGtsProtocol.java b/src/main/java/org/traccar/protocol/OpenGtsProtocol.java
index 5443b4ffc..24d6de706 100644
--- a/src/main/java/org/traccar/protocol/OpenGtsProtocol.java
+++ b/src/main/java/org/traccar/protocol/OpenGtsProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class OpenGtsProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/OrbcommProtocol.java b/src/main/java/org/traccar/protocol/OrbcommProtocol.java
index fb09f0abb..06b00619c 100644
--- a/src/main/java/org/traccar/protocol/OrbcommProtocol.java
+++ b/src/main/java/org/traccar/protocol/OrbcommProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerClient;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class OrbcommProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/OrbcommProtocolDecoder.java b/src/main/java/org/traccar/protocol/OrbcommProtocolDecoder.java
index 1164d72a1..7ed13d647 100644
--- a/src/main/java/org/traccar/protocol/OrbcommProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/OrbcommProtocolDecoder.java
@@ -24,10 +24,10 @@ 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;
diff --git a/src/main/java/org/traccar/protocol/OrionProtocol.java b/src/main/java/org/traccar/protocol/OrionProtocol.java
index 2dec7cd06..b78af462b 100644
--- a/src/main/java/org/traccar/protocol/OrionProtocol.java
+++ b/src/main/java/org/traccar/protocol/OrionProtocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class OrionProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/OsmAndProtocol.java b/src/main/java/org/traccar/protocol/OsmAndProtocol.java
index a86bc70d7..e06580949 100644
--- a/src/main/java/org/traccar/protocol/OsmAndProtocol.java
+++ b/src/main/java/org/traccar/protocol/OsmAndProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class OsmAndProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/OutsafeProtocol.java b/src/main/java/org/traccar/protocol/OutsafeProtocol.java
index 0099be456..159534883 100644
--- a/src/main/java/org/traccar/protocol/OutsafeProtocol.java
+++ b/src/main/java/org/traccar/protocol/OutsafeProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class OutsafeProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/OutsafeProtocolDecoder.java b/src/main/java/org/traccar/protocol/OutsafeProtocolDecoder.java
index 62b873be7..f71778412 100644
--- a/src/main/java/org/traccar/protocol/OutsafeProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/OutsafeProtocolDecoder.java
@@ -23,11 +23,11 @@ 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 9ad337f19..c509ad282 100644
--- a/src/main/java/org/traccar/protocol/OwnTracksProtocol.java
+++ b/src/main/java/org/traccar/protocol/OwnTracksProtocol.java
@@ -24,7 +24,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class OwnTracksProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java b/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java
index 71ac87168..e54d07fa7 100644
--- a/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java
@@ -25,8 +25,8 @@ 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 709729ef1..a315d4d9f 100644
--- a/src/main/java/org/traccar/protocol/PacificTrackProtocol.java
+++ b/src/main/java/org/traccar/protocol/PacificTrackProtocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class PacificTrackProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/PathAwayProtocol.java b/src/main/java/org/traccar/protocol/PathAwayProtocol.java
index 1d13eea95..a65740475 100644
--- a/src/main/java/org/traccar/protocol/PathAwayProtocol.java
+++ b/src/main/java/org/traccar/protocol/PathAwayProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class PathAwayProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/PiligrimProtocol.java b/src/main/java/org/traccar/protocol/PiligrimProtocol.java
index aa45a0def..9dd1bc491 100644
--- a/src/main/java/org/traccar/protocol/PiligrimProtocol.java
+++ b/src/main/java/org/traccar/protocol/PiligrimProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class PiligrimProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/PluginProtocol.java b/src/main/java/org/traccar/protocol/PluginProtocol.java
index b2101b18d..fff1830e8 100644
--- a/src/main/java/org/traccar/protocol/PluginProtocol.java
+++ b/src/main/java/org/traccar/protocol/PluginProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class PluginProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/PolteProtocol.java b/src/main/java/org/traccar/protocol/PolteProtocol.java
index 69666cc0e..0fbedfb09 100644
--- a/src/main/java/org/traccar/protocol/PolteProtocol.java
+++ b/src/main/java/org/traccar/protocol/PolteProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class PolteProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/PolteProtocolDecoder.java b/src/main/java/org/traccar/protocol/PolteProtocolDecoder.java
index 028de5424..8954db491 100644
--- a/src/main/java/org/traccar/protocol/PolteProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/PolteProtocolDecoder.java
@@ -23,8 +23,8 @@ 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 de78013fa..3a4b49289 100644
--- a/src/main/java/org/traccar/protocol/PortmanProtocol.java
+++ b/src/main/java/org/traccar/protocol/PortmanProtocol.java
@@ -24,7 +24,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class PortmanProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/PortmanProtocolDecoder.java b/src/main/java/org/traccar/protocol/PortmanProtocolDecoder.java
index da9403313..716f2694b 100644
--- a/src/main/java/org/traccar/protocol/PortmanProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/PortmanProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 - 2021 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.
@@ -34,7 +34,11 @@ public class PortmanProtocolDecoder extends BaseProtocolDecoder {
}
private static final Pattern PATTERN_STANDARD = new PatternBuilder()
+ .groupBegin()
.text("$PTMLA,") // header
+ .or()
+ .text("%%") // header
+ .groupEnd()
.expression("([^,]+),") // id
.expression("([ABCL]),") // validity
.number("(dd)(dd)(dd)") // date (yymmdd)
@@ -47,12 +51,19 @@ public class PortmanProtocolDecoder extends BaseProtocolDecoder {
.number("(d+),") // course
.number("(?:NA|C(-?d+)),") // temperature
.number("(x{8}),") // status
- .number("(?:NA|(d+)),") // card id
+ .groupBegin()
+ .text("NA")
+ .or()
+ .number("F(d+)") // fuel
+ .or()
+ .number("(d+)") // card id
+ .groupEnd(",")
.number("(d+),") // event
.number("(d+),") // satellites
.number("(d+.d+),") // odometer
- .number("(d+),") // rssi
- .number("(?:G(d+)|[^,]*)") // fuel
+ .number("(d+)") // rssi
+ .number(",G(d+)").optional() // fuel
+ .any()
.compile();
private Object decodeStandard(Channel channel, SocketAddress remoteAddress, String sentence) {
@@ -79,6 +90,9 @@ public class PortmanProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.PREFIX_TEMP + 1, parser.next());
position.set(Position.KEY_STATUS, parser.nextHexLong());
+ if (parser.hasNext()) {
+ position.set(Position.KEY_FUEL_LEVEL, parser.nextInt() * 0.1);
+ }
position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
int event = parser.nextInt();
@@ -159,7 +173,7 @@ public class PortmanProtocolDecoder extends BaseProtocolDecoder {
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
String sentence = (String) msg;
- if (sentence.startsWith("$PTMLA")) {
+ if (sentence.startsWith("%%") || sentence.startsWith("$PTMLA")) {
return decodeStandard(channel, remoteAddress, sentence);
} else if (sentence.startsWith("$EXT")) {
return decodeExtended(channel, remoteAddress, sentence);
diff --git a/src/main/java/org/traccar/protocol/PositrexProtocol.java b/src/main/java/org/traccar/protocol/PositrexProtocol.java
new file mode 100644
index 000000000..5cf389fbe
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/PositrexProtocol.java
@@ -0,0 +1,36 @@
+/*
+ * 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 jakarta.inject.Inject;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+public class PositrexProtocol extends BaseProtocol {
+
+ @Inject
+ public PositrexProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), true) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
+ pipeline.addLast(new PositrexProtocolDecoder(PositrexProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/PositrexProtocolDecoder.java b/src/main/java/org/traccar/protocol/PositrexProtocolDecoder.java
new file mode 100644
index 000000000..82ae2c134
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/PositrexProtocolDecoder.java
@@ -0,0 +1,116 @@
+/*
+ * 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.helper.BitUtil;
+import org.traccar.helper.DateBuilder;
+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 PositrexProtocolDecoder extends BaseProtocolDecoder {
+
+ public PositrexProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_PING = 0x2E;
+
+ private Date readTime(ByteBuf buf) {
+ long time = buf.readUnsignedInt();
+ DateBuilder dateBuilder = new DateBuilder();
+ dateBuilder.setSecond((int) (time % 60));
+ time /= 60;
+ dateBuilder.setMinute((int) (time % 60));
+ time /= 60;
+ dateBuilder.setHour((int) (time % 24));
+ time /= 24;
+ dateBuilder.setDay((int) (time % 32));
+ time /= 32;
+ dateBuilder.setMonth((int) (time % 13));
+ dateBuilder.setYear((int) (2000 + time / 13));
+ return dateBuilder.getDate();
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ int first = buf.getUnsignedByte(buf.readerIndex());
+ long deviceId;
+ if (BitUtil.check(first, 7)) {
+ if (BitUtil.check(first, 6)) {
+ deviceId = 73000000 + BitUtil.to(buf.readUnsignedInt(), 30);
+ } else if (!BitUtil.check(first, 5) && !BitUtil.check(first, 4)) {
+ deviceId = 7590000 + BitUtil.to(buf.readUnsignedMedium(), 20);
+ } else {
+ deviceId = 70000000 + BitUtil.to(buf.readUnsignedMedium(), 20);
+ }
+ } else {
+ deviceId = 7560000 + buf.readUnsignedShort();
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(deviceId));
+ if (deviceSession == null) {
+ return null;
+ }
+
+ int service = buf.readUnsignedByte();
+ if (service == MSG_PING) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setTime(readTime(buf));
+
+ int latitude = buf.readMedium();
+ int longitude = buf.readMedium();
+
+ position.setValid(BitUtil.check(latitude, 23));
+ position.setLatitude(BitUtil.to(latitude, 23) * 0.000025);
+ position.setLongitude(longitude * 0.000025);
+
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
+ position.setCourse(buf.readUnsignedByte() * 2);
+
+ position.set(Position.PREFIX_IO, buf.readUnsignedByte());
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+ buf.readUnsignedInt(); // report begin
+ buf.readUnsignedInt(); // report end
+ buf.readUnsignedInt(); // number of records
+
+ if (buf.isReadable()) {
+ position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01);
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.01);
+ }
+
+ return position;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/PretraceProtocol.java b/src/main/java/org/traccar/protocol/PretraceProtocol.java
index b77dd97bf..54a34fc69 100644
--- a/src/main/java/org/traccar/protocol/PretraceProtocol.java
+++ b/src/main/java/org/traccar/protocol/PretraceProtocol.java
@@ -24,7 +24,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class PretraceProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/PricolProtocol.java b/src/main/java/org/traccar/protocol/PricolProtocol.java
index f5e904541..7b0e7386c 100644
--- a/src/main/java/org/traccar/protocol/PricolProtocol.java
+++ b/src/main/java/org/traccar/protocol/PricolProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class PricolProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/ProgressProtocol.java b/src/main/java/org/traccar/protocol/ProgressProtocol.java
index 49eb6847f..8d159ef24 100644
--- a/src/main/java/org/traccar/protocol/ProgressProtocol.java
+++ b/src/main/java/org/traccar/protocol/ProgressProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import java.nio.ByteOrder;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class ProgressProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/PstProtocol.java b/src/main/java/org/traccar/protocol/PstProtocol.java
index 73f978cbd..01a83b31f 100644
--- a/src/main/java/org/traccar/protocol/PstProtocol.java
+++ b/src/main/java/org/traccar/protocol/PstProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class PstProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Pt215Protocol.java b/src/main/java/org/traccar/protocol/Pt215Protocol.java
index b272582a4..fd67b1241 100644
--- a/src/main/java/org/traccar/protocol/Pt215Protocol.java
+++ b/src/main/java/org/traccar/protocol/Pt215Protocol.java
@@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Pt215Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Pt3000Protocol.java b/src/main/java/org/traccar/protocol/Pt3000Protocol.java
index d72774f47..5f49084ed 100644
--- a/src/main/java/org/traccar/protocol/Pt3000Protocol.java
+++ b/src/main/java/org/traccar/protocol/Pt3000Protocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Pt3000Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Pt502Protocol.java b/src/main/java/org/traccar/protocol/Pt502Protocol.java
index d5d30e8e8..0257dd60f 100644
--- a/src/main/java/org/traccar/protocol/Pt502Protocol.java
+++ b/src/main/java/org/traccar/protocol/Pt502Protocol.java
@@ -22,7 +22,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Pt502Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Pt60Protocol.java b/src/main/java/org/traccar/protocol/Pt60Protocol.java
index 58345f025..83e3bfbeb 100644
--- a/src/main/java/org/traccar/protocol/Pt60Protocol.java
+++ b/src/main/java/org/traccar/protocol/Pt60Protocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Pt60Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/PuiProtocol.java b/src/main/java/org/traccar/protocol/PuiProtocol.java
new file mode 100644
index 000000000..ac8291039
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/PuiProtocol.java
@@ -0,0 +1,40 @@
+/*
+ * 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.mqtt.MqttDecoder;
+import io.netty.handler.codec.mqtt.MqttEncoder;
+import jakarta.inject.Inject;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+public class PuiProtocol extends BaseProtocol {
+
+ @Inject
+ public PuiProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
+ pipeline.addLast(MqttEncoder.INSTANCE);
+ pipeline.addLast(new MqttDecoder());
+ pipeline.addLast(new PuiProtocolDecoder(PuiProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/PuiProtocolDecoder.java b/src/main/java/org/traccar/protocol/PuiProtocolDecoder.java
new file mode 100644
index 000000000..f10ff3fe7
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/PuiProtocolDecoder.java
@@ -0,0 +1,73 @@
+/*
+ * 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.mqtt.MqttPublishMessage;
+import jakarta.json.Json;
+import jakarta.json.JsonObject;
+import org.apache.kafka.common.utils.ByteBufferInputStream;
+import org.traccar.BaseMqttProtocolDecoder;
+import org.traccar.Protocol;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+import org.traccar.session.DeviceSession;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+
+public class PuiProtocolDecoder extends BaseMqttProtocolDecoder {
+
+ public PuiProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(DeviceSession deviceSession, MqttPublishMessage message) throws Exception {
+
+ JsonObject json;
+ try (ByteBufferInputStream inputStream = new ByteBufferInputStream(message.payload().nioBuffer())) {
+ json = Json.createReader(inputStream).readObject();
+ }
+
+ String type = json.getString("rpt");
+ switch (type) {
+ case "hf":
+ case "loc":
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setValid(true);
+
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
+ position.setTime(dateFormat.parse(json.getString("ts")));
+
+ JsonObject location = json.getJsonObject("location");
+ position.setLatitude(location.getJsonNumber("lat").doubleValue());
+ position.setLongitude(location.getJsonNumber("lon").doubleValue());
+
+ position.setCourse(json.getInt("bear"));
+ position.setSpeed(UnitsConverter.knotsFromCps(json.getInt("spd")));
+
+ position.set(Position.KEY_IGNITION, json.getString("ign").equals("on"));
+
+ return position;
+
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/R12wProtocol.java b/src/main/java/org/traccar/protocol/R12wProtocol.java
index a406f6306..b5b3eff81 100644
--- a/src/main/java/org/traccar/protocol/R12wProtocol.java
+++ b/src/main/java/org/traccar/protocol/R12wProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class R12wProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/RaceDynamicsProtocol.java b/src/main/java/org/traccar/protocol/RaceDynamicsProtocol.java
index 63ca3476c..6f7340902 100644
--- a/src/main/java/org/traccar/protocol/RaceDynamicsProtocol.java
+++ b/src/main/java/org/traccar/protocol/RaceDynamicsProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class RaceDynamicsProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/RadarProtocol.java b/src/main/java/org/traccar/protocol/RadarProtocol.java
index 9d88c6d72..8985e0e83 100644
--- a/src/main/java/org/traccar/protocol/RadarProtocol.java
+++ b/src/main/java/org/traccar/protocol/RadarProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class RadarProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/RamacProtocol.java b/src/main/java/org/traccar/protocol/RamacProtocol.java
new file mode 100644
index 000000000..42ce16fe8
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/RamacProtocol.java
@@ -0,0 +1,42 @@
+/*
+ * 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 jakarta.inject.Inject;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+public class RamacProtocol extends BaseProtocol {
+
+ @Inject
+ public RamacProtocol(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 RamacProtocolDecoder(RamacProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/RamacProtocolDecoder.java b/src/main/java/org/traccar/protocol/RamacProtocolDecoder.java
new file mode 100644
index 000000000..ffdc68474
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/RamacProtocolDecoder.java
@@ -0,0 +1,112 @@
+/*
+ * 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.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.HttpResponseStatus;
+import jakarta.json.Json;
+import jakarta.json.JsonObject;
+import org.traccar.BaseHttpProtocolDecoder;
+import org.traccar.Protocol;
+import org.traccar.model.Position;
+import org.traccar.session.DeviceSession;
+
+import java.io.StringReader;
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+
+public class RamacProtocolDecoder extends BaseHttpProtocolDecoder {
+
+ private final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+ public RamacProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ FullHttpRequest request = (FullHttpRequest) msg;
+ String content = request.content().toString(StandardCharsets.UTF_8);
+ JsonObject json = Json.createReader(new StringReader(content)).readObject();
+
+ String deviceId = json.getString("DeviceId");
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, deviceId);
+ if (deviceSession == null) {
+ sendResponse(channel, HttpResponseStatus.BAD_REQUEST);
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.set(Position.KEY_TYPE, json.getInt("PacketType"));
+ position.set(Position.KEY_INDEX, json.getInt("SeqNumber"));
+ position.setDeviceTime(dateFormat.parse(json.getString("UpdateDate")));
+
+ int alert = json.getInt("Alert");
+ if (alert > 0) {
+ position.set("alert", alert);
+ String alertMessage = json.getString("AlertMessage");
+ if (!alertMessage.isEmpty()) {
+ position.set("alertMessage", alertMessage);
+ }
+ }
+
+ if (json.containsKey("GpsEvent")) {
+ position.set("gpsEvent", json.getInt("GpsEvent"));
+ if (json.containsKey("GpsEventText")) {
+ position.set("gpsEventText", json.getString("GpsEventText"));
+ }
+ }
+
+ if (json.containsKey("Event")) {
+ position.set(Position.KEY_EVENT, json.getInt("Event"));
+ }
+ if (json.containsKey("BatteryPercentage")) {
+ position.set(Position.KEY_BATTERY_LEVEL, json.getInt("BatteryPercentage"));
+ }
+ if (json.containsKey("Battery")) {
+ position.set(Position.KEY_BATTERY, json.getJsonNumber("Battery").doubleValue());
+ }
+
+ position.set("deviceType", json.getString("DeviceTypeText"));
+
+ if (json.containsKey("Latitude") && json.containsKey("Longitude")) {
+ position.setValid(true);
+ if (json.containsKey("LocationDateTime")) {
+ position.setFixTime(dateFormat.parse(json.getString("LocationDateTime")));
+ } else {
+ position.setFixTime(position.getDeviceTime());
+ }
+ position.setLatitude(json.getJsonNumber("Latitude").doubleValue());
+ position.setLongitude(json.getJsonNumber("Longitude").doubleValue());
+ } else {
+ getLastLocation(position, position.getDeviceTime());
+ }
+
+ sendResponse(
+ channel, HttpResponseStatus.OK,
+ Unpooled.copiedBuffer("{\"CaseID\":1,\"EventID\":1}", StandardCharsets.UTF_8));
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/RaveonProtocol.java b/src/main/java/org/traccar/protocol/RaveonProtocol.java
index db70396ee..aa1a79219 100644
--- a/src/main/java/org/traccar/protocol/RaveonProtocol.java
+++ b/src/main/java/org/traccar/protocol/RaveonProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class RaveonProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/RecodaProtocol.java b/src/main/java/org/traccar/protocol/RecodaProtocol.java
index 0d50db01e..7d2fadae4 100644
--- a/src/main/java/org/traccar/protocol/RecodaProtocol.java
+++ b/src/main/java/org/traccar/protocol/RecodaProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import java.nio.ByteOrder;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class RecodaProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/RetranslatorProtocol.java b/src/main/java/org/traccar/protocol/RetranslatorProtocol.java
index 1d4b419bb..a349a8191 100644
--- a/src/main/java/org/traccar/protocol/RetranslatorProtocol.java
+++ b/src/main/java/org/traccar/protocol/RetranslatorProtocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class RetranslatorProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/RfTrackProtocol.java b/src/main/java/org/traccar/protocol/RfTrackProtocol.java
index d3b41e93e..ac033c348 100644
--- a/src/main/java/org/traccar/protocol/RfTrackProtocol.java
+++ b/src/main/java/org/traccar/protocol/RfTrackProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class RfTrackProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/RfTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/RfTrackProtocolDecoder.java
index 28a3ac29c..cbb204e3b 100644
--- a/src/main/java/org/traccar/protocol/RfTrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/RfTrackProtocolDecoder.java
@@ -29,9 +29,9 @@ import org.traccar.model.Position;
import org.traccar.model.WifiAccessPoint;
import org.traccar.session.DeviceSession;
-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/RitiProtocol.java b/src/main/java/org/traccar/protocol/RitiProtocol.java
index 9b9c00cb2..9916042a8 100644
--- a/src/main/java/org/traccar/protocol/RitiProtocol.java
+++ b/src/main/java/org/traccar/protocol/RitiProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import java.nio.ByteOrder;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class RitiProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/RoboTrackProtocol.java b/src/main/java/org/traccar/protocol/RoboTrackProtocol.java
index ab2bc5842..229c343bb 100644
--- a/src/main/java/org/traccar/protocol/RoboTrackProtocol.java
+++ b/src/main/java/org/traccar/protocol/RoboTrackProtocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class RoboTrackProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/RstProtocol.java b/src/main/java/org/traccar/protocol/RstProtocol.java
index 109d91b16..0bb809a49 100644
--- a/src/main/java/org/traccar/protocol/RstProtocol.java
+++ b/src/main/java/org/traccar/protocol/RstProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class RstProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/RstProtocolDecoder.java b/src/main/java/org/traccar/protocol/RstProtocolDecoder.java
index fcc96fbf1..eafa4d3d7 100644
--- a/src/main/java/org/traccar/protocol/RstProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/RstProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2024 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -69,8 +69,10 @@ public class RstProtocolDecoder extends BaseProtocolDecoder {
.number("x{4};") // sensors
.number("(xx);") // status 1
.number("(xx);") // status 2
+ .expression("(.*)") // additional data
.groupEnd("?")
.any()
+ .text("FIM;")
.compile();
@Override
@@ -115,9 +117,16 @@ public class RstProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_SATELLITES, parser.nextInt());
position.set(Position.KEY_HDOP, parser.nextInt());
- position.set(Position.PREFIX_IN + 1, parser.nextHexInt());
- position.set(Position.PREFIX_IN + 2, parser.nextHexInt());
- position.set(Position.PREFIX_IN + 3, parser.nextHexInt());
+
+ int inputs1 = parser.nextHexInt();
+ int inputs2 = parser.nextHexInt();
+ int inputs3 = parser.nextHexInt();
+ position.set(Position.PREFIX_IN + 1, inputs1);
+ position.set(Position.PREFIX_IN + 2, inputs2);
+ position.set(Position.PREFIX_IN + 3, inputs3);
+
+ position.set(Position.KEY_IGNITION, BitUtil.check(inputs2, 7));
+
position.set(Position.PREFIX_OUT + 1, parser.nextHexInt());
position.set(Position.PREFIX_OUT + 2, parser.nextHexInt());
position.set(Position.KEY_POWER, parser.nextDouble());
@@ -125,10 +134,23 @@ public class RstProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ODOMETER, parser.nextInt());
position.set(Position.KEY_RSSI, parser.nextInt());
position.set(Position.PREFIX_TEMP + 1, (int) parser.nextHexInt().byteValue());
+ position.set(Position.KEY_STATUS, (parser.nextHexInt() << 8) + parser.nextHexInt());
+
+ String[] values = parser.next().split(";");
+ if (type == 55) {
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, values[0]);
+ }
+
+ return position;
+
+ } else if (type == 134) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
- int status = (parser.nextHexInt() << 8) + parser.nextHexInt();
- position.set(Position.KEY_IGNITION, BitUtil.check(status, 7));
- position.set(Position.KEY_STATUS, status);
+ position.set(Position.KEY_RESULT, String.valueOf(type));
return position;
diff --git a/src/main/java/org/traccar/protocol/RuptelaProtocol.java b/src/main/java/org/traccar/protocol/RuptelaProtocol.java
index 99a9686f6..9f399e299 100644
--- a/src/main/java/org/traccar/protocol/RuptelaProtocol.java
+++ b/src/main/java/org/traccar/protocol/RuptelaProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class RuptelaProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java b/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java
index 77df0deb7..e1efb5757 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 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2024 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@ import org.traccar.BaseProtocolDecoder;
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 +51,7 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder {
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_HEARTBEAT = 16;
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 +94,55 @@ 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 13:
+ case 173:
+ position.set(Position.KEY_MOTION, readValue(buf, length, false) > 0);
+ 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 39:
+ position.set(Position.KEY_ENGINE_LOAD, readValue(buf, length, false));
+ break;
+ case 65:
+ position.set(Position.KEY_ODOMETER, readValue(buf, length, false));
+ break;
case 74:
position.set(Position.PREFIX_TEMP + 3, readValue(buf, length, true) * 0.1);
break;
@@ -116,6 +151,23 @@ 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 94:
+ position.set(Position.KEY_RPM, readValue(buf, length, false) * 0.25);
+ break;
+ case 95:
+ position.set(Position.KEY_OBD_SPEED, readValue(buf, length, false));
+ break;
+ case 98:
+ position.set(Position.KEY_FUEL_LEVEL, readValue(buf, length, false) * 100 / 255.0);
+ break;
+ case 100:
+ position.set(Position.KEY_FUEL_CONSUMPTION, readValue(buf, length, false) / 20.0);
+ break;
case 134:
if (readValue(buf, length, false) > 0) {
position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
@@ -126,9 +178,61 @@ 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 163:
+ position.set(Position.KEY_ODOMETER, readValue(buf, length, false) * 5);
+ break;
+ case 164:
+ position.set(Position.KEY_ODOMETER_TRIP, readValue(buf, length, false) * 5);
+ break;
+ case 165:
+ position.set(Position.KEY_OBD_SPEED, readValue(buf, length, false) / 256.0);
+ break;
+ case 166:
case 197:
position.set(Position.KEY_RPM, readValue(buf, length, false) * 0.125);
break;
+ case 170:
+ position.set(Position.KEY_CHARGE, readValue(buf, length, false) > 0);
+ break;
+ case 205:
+ position.set(Position.KEY_FUEL_LEVEL, readValue(buf, length, false));
+ break;
+ case 207:
+ position.set(Position.KEY_FUEL_LEVEL, readValue(buf, length, false) * 0.4);
+ break;
+ case 208:
+ position.set(Position.KEY_FUEL_USED, readValue(buf, length, false) * 0.5);
+ 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, false) * 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 +270,36 @@ 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) {
+ if (positions.size() == 0) {
+ getLastLocation(position, null);
+ } else {
+ 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 +335,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);
@@ -306,7 +425,7 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder {
return null;
- } else if (type == MSG_IDENTIFICATION) {
+ } else if (type == MSG_IDENTIFICATION || type == MSG_HEARTBEAT) {
ByteBuf content = Unpooled.buffer();
content.writeByte(1);
diff --git a/src/main/java/org/traccar/protocol/S168Protocol.java b/src/main/java/org/traccar/protocol/S168Protocol.java
index f904ed9ff..5fb0c6e72 100644
--- a/src/main/java/org/traccar/protocol/S168Protocol.java
+++ b/src/main/java/org/traccar/protocol/S168Protocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class S168Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/SabertekProtocol.java b/src/main/java/org/traccar/protocol/SabertekProtocol.java
index 403243cdc..cb3f2ab32 100644
--- a/src/main/java/org/traccar/protocol/SabertekProtocol.java
+++ b/src/main/java/org/traccar/protocol/SabertekProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class SabertekProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/SanavProtocol.java b/src/main/java/org/traccar/protocol/SanavProtocol.java
index 1a0e7b0e9..ac1941725 100644
--- a/src/main/java/org/traccar/protocol/SanavProtocol.java
+++ b/src/main/java/org/traccar/protocol/SanavProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class SanavProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/SanulProtocol.java b/src/main/java/org/traccar/protocol/SanulProtocol.java
index ea44bf868..cba162296 100644
--- a/src/main/java/org/traccar/protocol/SanulProtocol.java
+++ b/src/main/java/org/traccar/protocol/SanulProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.config.Config;
import java.nio.ByteOrder;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class SanulProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/SatsolProtocol.java b/src/main/java/org/traccar/protocol/SatsolProtocol.java
index d90033e38..7252f99f0 100644
--- a/src/main/java/org/traccar/protocol/SatsolProtocol.java
+++ b/src/main/java/org/traccar/protocol/SatsolProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.config.Config;
import java.nio.ByteOrder;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class SatsolProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/SigfoxProtocol.java b/src/main/java/org/traccar/protocol/SigfoxProtocol.java
index 9a268af62..edd624727 100644
--- a/src/main/java/org/traccar/protocol/SigfoxProtocol.java
+++ b/src/main/java/org/traccar/protocol/SigfoxProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class SigfoxProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java b/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java
index 4ed2bb51d..e0dfab9b2 100644
--- a/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2024 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,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;
@@ -105,7 +105,7 @@ public class SigfoxProtocolDecoder extends BaseHttpProtocolDecoder {
FullHttpRequest request = (FullHttpRequest) msg;
String content = request.content().toString(StandardCharsets.UTF_8);
if (!content.startsWith("{")) {
- content = URLDecoder.decode(content.split("=")[0], "UTF-8");
+ content = URLDecoder.decode(content.split("=")[0], StandardCharsets.UTF_8);
}
JsonObject json = Json.createReader(new StringReader(content)).readObject();
@@ -156,18 +156,30 @@ public class SigfoxProtocolDecoder extends BaseHttpProtocolDecoder {
ByteBuf buf = Unpooled.wrappedBuffer(DataConverter.parseHex(json.getString("data")));
try {
- int event = buf.readUnsignedByte();
- if (event == 0x0f || event == 0x1f) {
+ int header = buf.readUnsignedByte();
+ if ("Amber".equals(getDeviceModel(deviceSession))) {
+
+ int flags = buf.readUnsignedByte();
+ position.set(Position.KEY_MOTION, BitUtil.check(flags, 1));
+
+ position.set(Position.KEY_BATTERY, buf.readUnsignedByte() * 0.02);
+ position.set(Position.PREFIX_TEMP + 1, (int) buf.readByte());
+
+ position.setValid(true);
+ position.setLatitude(buf.readInt() / 60000.0);
+ position.setLongitude(buf.readInt() / 60000.0);
+
+ } else if (header == 0x0f || header == 0x1f) {
- position.setValid(event >> 4 > 0);
+ position.setValid(header >> 4 > 0);
position.setLatitude(BufferUtil.readSignedMagnitudeInt(buf) * 0.000001);
position.setLongitude(BufferUtil.readSignedMagnitudeInt(buf) * 0.000001);
position.set(Position.KEY_BATTERY, (int) buf.readUnsignedByte());
- } else if (event >> 4 <= 3 && buf.writerIndex() == 12) {
+ } else if (header >> 4 <= 3 && buf.writerIndex() == 12) {
- if (BitUtil.to(event, 4) == 0) {
+ if (BitUtil.to(header, 4) == 0) {
position.setValid(true);
position.setLatitude(buf.readIntLE() * 0.0000001);
position.setLongitude(buf.readIntLE() * 0.0000001);
diff --git a/src/main/java/org/traccar/protocol/SiwiProtocol.java b/src/main/java/org/traccar/protocol/SiwiProtocol.java
index f12958a50..59b96bf72 100644
--- a/src/main/java/org/traccar/protocol/SiwiProtocol.java
+++ b/src/main/java/org/traccar/protocol/SiwiProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class SiwiProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/SkypatrolProtocol.java b/src/main/java/org/traccar/protocol/SkypatrolProtocol.java
index 7ae26f634..615ef536d 100644
--- a/src/main/java/org/traccar/protocol/SkypatrolProtocol.java
+++ b/src/main/java/org/traccar/protocol/SkypatrolProtocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class SkypatrolProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/SmartSoleProtocol.java b/src/main/java/org/traccar/protocol/SmartSoleProtocol.java
index cb7efb7ee..e4838581a 100644
--- a/src/main/java/org/traccar/protocol/SmartSoleProtocol.java
+++ b/src/main/java/org/traccar/protocol/SmartSoleProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class SmartSoleProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/SmokeyProtocol.java b/src/main/java/org/traccar/protocol/SmokeyProtocol.java
index 22b343537..0aa2bcfa7 100644
--- a/src/main/java/org/traccar/protocol/SmokeyProtocol.java
+++ b/src/main/java/org/traccar/protocol/SmokeyProtocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class SmokeyProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/SolarPoweredProtocol.java b/src/main/java/org/traccar/protocol/SolarPoweredProtocol.java
index 0676aa629..e00f27b9b 100644
--- a/src/main/java/org/traccar/protocol/SolarPoweredProtocol.java
+++ b/src/main/java/org/traccar/protocol/SolarPoweredProtocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class SolarPoweredProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/SpotProtocol.java b/src/main/java/org/traccar/protocol/SpotProtocol.java
index 6bd802fed..4fc57f177 100644
--- a/src/main/java/org/traccar/protocol/SpotProtocol.java
+++ b/src/main/java/org/traccar/protocol/SpotProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class SpotProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/StarLinkProtocol.java b/src/main/java/org/traccar/protocol/StarLinkProtocol.java
index d578fa705..6dcd40fbf 100644
--- a/src/main/java/org/traccar/protocol/StarLinkProtocol.java
+++ b/src/main/java/org/traccar/protocol/StarLinkProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class StarLinkProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java b/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java
index aa23bfac5..193005e28 100644
--- a/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java
@@ -61,10 +61,10 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
@Override
protected void init() {
setFormat(getConfig().getString(
- getProtocolName() + ".format", "#EDT#,#EID#,#PDT#,#LAT#,#LONG#,#SPD#,#HEAD#,#ODO#,"
+ 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(getConfig().getString(getProtocolName() + ".dateFormat", "yyMMddHHmmss"));
+ setDateFormat(getConfig().getString(Keys.PROTOCOL_DATE_FORMAT.withPrefix(getProtocolName()), "yyMMddHHmmss"));
}
public String[] getFormat(long deviceId) {
diff --git a/src/main/java/org/traccar/protocol/StarcomProtocol.java b/src/main/java/org/traccar/protocol/StarcomProtocol.java
index 33c3a4776..458220e59 100644
--- a/src/main/java/org/traccar/protocol/StarcomProtocol.java
+++ b/src/main/java/org/traccar/protocol/StarcomProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class StarcomProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/StarcomProtocolDecoder.java b/src/main/java/org/traccar/protocol/StarcomProtocolDecoder.java
index 56ab733c8..325847b16 100644
--- a/src/main/java/org/traccar/protocol/StarcomProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/StarcomProtocolDecoder.java
@@ -75,7 +75,7 @@ public class StarcomProtocolDecoder extends BaseProtocolDecoder {
case "eventid":
position.set(Position.KEY_EVENT, Integer.parseInt(value));
break;
- case "mileage":
+ case "odometer":
position.set(Position.KEY_ODOMETER, (long) (Double.parseDouble(value) * 1000));
break;
case "satellites":
@@ -110,8 +110,8 @@ public class StarcomProtocolDecoder extends BaseProtocolDecoder {
case "extra1":
case "extra2":
case "extra3":
- position.set(key, value);
default:
+ position.set(key, value);
break;
}
}
diff --git a/src/main/java/org/traccar/protocol/StartekFrameDecoder.java b/src/main/java/org/traccar/protocol/StartekFrameDecoder.java
new file mode 100644
index 000000000..608b128cd
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/StartekFrameDecoder.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2024 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
+
+import java.nio.charset.StandardCharsets;
+
+public class StartekFrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ if (buf.readableBytes() < 10) {
+ return null;
+ }
+
+ int lengthIndex = buf.readerIndex() + 3;
+ int dividerIndex = buf.indexOf(lengthIndex, buf.writerIndex(), (byte) ',');
+ if (dividerIndex > 0) {
+ int lengthOffset = dividerIndex - buf.readerIndex() + 4;
+ int length = lengthOffset + Integer.parseInt(buf.getCharSequence(
+ lengthIndex, dividerIndex - lengthIndex, StandardCharsets.US_ASCII).toString());
+ if (buf.readableBytes() >= length) {
+ return buf.readRetainedSlice(length);
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/StartekProtocol.java b/src/main/java/org/traccar/protocol/StartekProtocol.java
index d010df858..9c01d24e2 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 - 2024 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,7 +15,6 @@
*/
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;
@@ -24,7 +23,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class StartekProtocol extends BaseProtocol {
@@ -38,7 +37,7 @@ public class StartekProtocol extends BaseProtocol {
addServer(new TrackerServer(config, getName(), false) {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
- pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StartekFrameDecoder());
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 8e3624cb5..0eeb5b2aa 100644
--- a/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2021 - 2024 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -41,12 +41,13 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder {
.expression(".") // index
.number("d+,") // length
.number("(d+),") // imei
+ .number("(xxx),") // type
.expression("(.+)") // content
.number("xx") // checksum
+ .text("\r\n")
.compile();
private static final Pattern PATTERN_POSITION = new PatternBuilder()
- .number("xxx,") // command
.number("(d+),") // event
.expression("([^,]+)?,") // event data
.number("(dd)(dd)(dd)") // date (yyymmdd)
@@ -72,12 +73,10 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder {
.number("(x+)") // battery
.expression("([^,]+)?") // adc
.groupBegin()
- .text(",")
- .number("d,") // extended
- .expression("([^,]+)?") // fuel
+ .number(",d+") // extended
+ .expression(",([^,]+)?") // fuel
.groupBegin()
- .text(",")
- .expression("([^,]+)?") // temperature
+ .expression(",([^,]+)?") // temperature
.groupBegin()
.text(",")
.groupBegin()
@@ -91,16 +90,26 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder {
.number("(d+)?|") // instant fuel
.number("(d+)[%L]").optional() // fuel level
.groupEnd("?")
+ .number(",(d+)").optional() // hours
.groupEnd("?")
.groupEnd("?")
.groupEnd("?")
+ .any()
.compile();
private String decodeAlarm(int value) {
switch (value) {
+ case 1:
+ return Position.ALARM_SOS;
case 5:
case 6:
return Position.ALARM_DOOR;
+ case 17:
+ return Position.ALARM_LOW_POWER;
+ case 18:
+ return Position.ALARM_POWER_CUT;
+ case 19:
+ return Position.ALARM_POWER_RESTORED;
case 39:
return Position.ALARM_ACCELERATION;
case 40:
@@ -126,26 +135,24 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder {
return null;
}
+ String type = parser.next();
String content = parser.next();
- if (content.length() < 100) {
-
- Position position = new Position(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
-
- getLastLocation(position, null);
-
- position.set(Position.KEY_RESULT, content);
-
- return position;
-
- } else {
-
- return decodePosition(deviceSession, content);
-
+ switch (type) {
+ case "000":
+ return decodePosition(deviceSession, content);
+ case "710":
+ return decodeSerial(deviceSession, content);
+ default:
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ getLastLocation(position, null);
+ position.set(Position.KEY_TYPE, type);
+ position.set(Position.KEY_RESULT, content);
+ return position;
}
}
- protected Object decodePosition(DeviceSession deviceSession, String content) throws Exception {
+ private Object decodePosition(DeviceSession deviceSession, String content) {
Parser parser = new Parser(PATTERN_POSITION, content);
if (!parser.matches()) {
@@ -176,7 +183,7 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder {
position.setCourse(parser.nextInt());
position.setAltitude(parser.nextInt());
- position.set(Position.KEY_ODOMETER, parser.nextInt());
+ position.set(Position.KEY_ODOMETER, parser.nextLong());
position.setNetwork(new Network(CellTower.from(
parser.nextInt(), parser.nextInt(), parser.nextHexInt(), parser.nextHexInt(), parser.nextInt())));
@@ -186,6 +193,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);
@@ -221,7 +229,7 @@ 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());
@@ -239,6 +247,87 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_FUEL_LEVEL, parser.nextInt());
}
+ if (parser.hasNext()) {
+ position.set(Position.KEY_HOURS, parser.nextInt() * 1000L);
+ }
+
+ return position;
+ }
+
+ private Object decodeSerial(DeviceSession deviceSession, String content) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ String[] frames = content.split("\r\n");
+
+ for (String frame : frames) {
+ String[] values = frame.split(",");
+ int index = 0;
+ String type = values[index++];
+ switch (type) {
+ case "T1":
+ index += 1; // speed
+ position.set(Position.KEY_RPM, Double.parseDouble(values[index++]));
+ index += 1; // fuel consumption
+ position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(values[index++]));
+ index += 4; // axel weights
+ index += 1; // turbo pressure
+ position.set(Position.KEY_COOLANT_TEMP, Integer.parseInt(values[index++]));
+ index += 1; // accelerator pedal
+ position.set("torque", Integer.parseInt(values[index++]));
+ index += 1; // firmware version
+ position.set(Position.KEY_POWER, Double.parseDouble(values[index++]));
+ index += 1; // coolant level
+ position.set("oilTemp", Double.parseDouble(values[index++]));
+ index += 1; // oil level
+ position.set(Position.KEY_THROTTLE, Double.parseDouble(values[index++]));
+ index += 1; // air inlet pressure
+ index += 1; // fuel tank secondary
+ index += 1; // current gear
+ index += 1; // seatbelt
+ position.set("oilPressure", Integer.parseInt(values[index++]));
+ index += 1; // wet tank air pressure
+ index += 1; // pto state
+ int ignition = Integer.parseInt(values[index++]);
+ if (ignition < 2) {
+ position.set(Position.KEY_IGNITION, ignition > 0);
+ }
+ index += 1; // brake pedal
+ position.set("catalystLevel", Double.parseDouble(values[index++]));
+ index += 1; // fuel type
+ break;
+ case "T2":
+ position.set(Position.KEY_ODOMETER, Double.parseDouble(values[index++]) * 1000);
+ index += 1; // total fuel
+ index += 1; // fuel used cruise
+ index += 1; // fuel used drive
+ index += 1;
+ index += 1;
+ index += 1; // total idle time
+ index += 1; // total time pto
+ index += 1; // time cruise
+ index += 1;
+ index += 1;
+ index += 1;
+ index += 1;
+ index += 1;
+ index += 1; // brake apps
+ index += 1; // clutch apps
+ position.set(Position.KEY_HOURS, Integer.parseInt(values[index++]));
+ index += 1; // time torque
+ position.set(Position.KEY_FUEL_CONSUMPTION, Double.parseDouble(values[index++]));
+ index += 1; // total cruise control distance
+ position.set(Position.KEY_FUEL_USED, Double.parseDouble(values[index++]));
+ index += 1; // total drive time
+ break;
+ default:
+ break;
+ }
+ }
+
return position;
}
diff --git a/src/main/java/org/traccar/protocol/StbProtocol.java b/src/main/java/org/traccar/protocol/StbProtocol.java
index af4e0d2c4..0beaed39c 100644
--- a/src/main/java/org/traccar/protocol/StbProtocol.java
+++ b/src/main/java/org/traccar/protocol/StbProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class StbProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/StbProtocolDecoder.java b/src/main/java/org/traccar/protocol/StbProtocolDecoder.java
index 641359bfd..c52ab485f 100644
--- a/src/main/java/org/traccar/protocol/StbProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/StbProtocolDecoder.java
@@ -22,9 +22,9 @@ 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;
diff --git a/src/main/java/org/traccar/protocol/Stl060Protocol.java b/src/main/java/org/traccar/protocol/Stl060Protocol.java
index 83b5db3bb..ac23ab3ee 100644
--- a/src/main/java/org/traccar/protocol/Stl060Protocol.java
+++ b/src/main/java/org/traccar/protocol/Stl060Protocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Stl060Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/SuntechProtocol.java b/src/main/java/org/traccar/protocol/SuntechProtocol.java
index 4253b761b..0cc5fc75c 100644
--- a/src/main/java/org/traccar/protocol/SuntechProtocol.java
+++ b/src/main/java/org/traccar/protocol/SuntechProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class SuntechProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
index 047a1822a..53c4a5d02 100644
--- a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
@@ -271,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;
}
@@ -446,9 +454,10 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
if (values.length - index >= 2) {
String driverUniqueId = values[index++];
- if (values[index++].equals("1") && !driverUniqueId.isEmpty()) {
+ if (!driverUniqueId.isEmpty()) {
position.set(Position.KEY_DRIVER_UNIQUE_ID, driverUniqueId);
}
+ index += 1; // registered
}
if (isIncludeTemp(deviceSession.getDeviceId())) {
diff --git a/src/main/java/org/traccar/protocol/SupermateProtocol.java b/src/main/java/org/traccar/protocol/SupermateProtocol.java
index 4290b7126..064f12b4b 100644
--- a/src/main/java/org/traccar/protocol/SupermateProtocol.java
+++ b/src/main/java/org/traccar/protocol/SupermateProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class SupermateProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/SviasProtocol.java b/src/main/java/org/traccar/protocol/SviasProtocol.java
index 7c6624f7c..a903d503c 100644
--- a/src/main/java/org/traccar/protocol/SviasProtocol.java
+++ b/src/main/java/org/traccar/protocol/SviasProtocol.java
@@ -24,7 +24,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class SviasProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/SwiftechProtocol.java b/src/main/java/org/traccar/protocol/SwiftechProtocol.java
index 68cf40d84..d5fa5c5d3 100644
--- a/src/main/java/org/traccar/protocol/SwiftechProtocol.java
+++ b/src/main/java/org/traccar/protocol/SwiftechProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class SwiftechProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/T55Protocol.java b/src/main/java/org/traccar/protocol/T55Protocol.java
index cedac275f..e76959fea 100644
--- a/src/main/java/org/traccar/protocol/T55Protocol.java
+++ b/src/main/java/org/traccar/protocol/T55Protocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class T55Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java b/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java
index 3be161fb8..9e7518ce5 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 - 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.
@@ -41,7 +41,8 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
}
private static final Pattern PATTERN_GPRMC = new PatternBuilder()
- .text("$GPRMC,")
+ .text("$")
+ .expression("G[PLN]RMC,")
.number("(dd)(dd)(dd).?d*,") // time (hhmmss)
.expression("([AV]),") // validity
.number("(dd)(dd.d+),") // latitude
@@ -64,7 +65,8 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
.compile();
private static final Pattern PATTERN_GPGGA = new PatternBuilder()
- .text("$GPGGA,")
+ .text("$")
+ .expression("G[PLN]GGA,")
.number("(dd)(dd)(dd).?d*,") // time (hhmmss)
.number("(d+)(dd.d+),") // latitude
.expression("([NS]),")
@@ -150,6 +152,18 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
.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(
@@ -328,6 +342,31 @@ 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);
@@ -407,9 +446,9 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
}
} else if (sentence.matches("^[0-9A-F]+$")) {
getDeviceSession(channel, remoteAddress, sentence);
- } else if (sentence.startsWith("$GPRMC")) {
+ } else if (sentence.startsWith("RMC", 3)) {
return decodeGprmc(deviceSession, sentence, remoteAddress, channel);
- } else if (sentence.startsWith("$GPGGA") && deviceSession != null) {
+ } else if (sentence.startsWith("GGA", 3) && deviceSession != null) {
return decodeGpgga(deviceSession, sentence);
} else if (sentence.startsWith("$GPRMA") && deviceSession != null) {
return decodeGprma(deviceSession, sentence);
@@ -421,6 +460,8 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
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 4bafe8c6d..e6ef4ccc9 100644
--- a/src/main/java/org/traccar/protocol/T57Protocol.java
+++ b/src/main/java/org/traccar/protocol/T57Protocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class T57Protocol extends BaseProtocol {
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 253c3cb73..f50f22a18 100644
--- a/src/main/java/org/traccar/protocol/T800xProtocol.java
+++ b/src/main/java/org/traccar/protocol/T800xProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class T800xProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java b/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java
index 6e09e6e3b..23750be8d 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,6 +20,8 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
+import org.traccar.config.Keys;
+import org.traccar.helper.model.AttributeUtil;
import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
@@ -161,7 +163,7 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
boolean positionType = type == MSG_GPS || type == MSG_GPS_2 || type == MSG_ALARM || type == MSG_ALARM_2;
if (!positionType) {
- sendResponse(channel, header, type, index, imei, 0);
+ sendResponse(channel, header, type, header == 0x2323 ? 1 : index, imei, 0);
}
if (positionType) {
@@ -389,7 +391,7 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
for (int i = 1; i <= adcCount; i++) {
String value = ByteBufUtil.hexDump(buf.readSlice(2));
if (!value.equals("ffff")) {
- position.set(Position.PREFIX_ADC + i, Integer.parseInt(value) * 0.01);
+ position.set(Position.PREFIX_ADC + i, Integer.parseInt(value, 16) * 0.01);
}
}
}
@@ -398,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) {
@@ -468,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
@@ -511,8 +515,10 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
}
}
- if (type == MSG_ALARM || type == MSG_ALARM_2) {
- sendResponse(channel, header, type, index, imei, alarm);
+ 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, header == 0x2323 ? 1 : index, imei, alarm);
}
return position;
diff --git a/src/main/java/org/traccar/protocol/TaipPrefixEncoder.java b/src/main/java/org/traccar/protocol/TaipPrefixEncoder.java
index 48419af2a..75fd447d0 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 - 2022 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.
@@ -15,40 +15,20 @@
*/
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.Protocol;
-import org.traccar.config.Config;
-import org.traccar.config.Keys;
import java.util.List;
@ChannelHandler.Sharable
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) {
- 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());
- }
+ out.add(Unpooled.wrappedBuffer(Unpooled.wrappedBuffer(new byte[] {0x20, 0x20, 0x06, 0x00}), msg.retain()));
}
}
diff --git a/src/main/java/org/traccar/protocol/TaipProtocol.java b/src/main/java/org/traccar/protocol/TaipProtocol.java
index 943ec98c5..f57bb296c 100644
--- a/src/main/java/org/traccar/protocol/TaipProtocol.java
+++ b/src/main/java/org/traccar/protocol/TaipProtocol.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.
@@ -23,7 +23,8 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
+import org.traccar.config.Keys;
public class TaipProtocol extends BaseProtocol {
@@ -33,7 +34,9 @@ public class TaipProtocol extends BaseProtocol {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '<'));
- pipeline.addLast(new TaipPrefixEncoder(TaipProtocol.this));
+ if (config.getBoolean(Keys.PROTOCOL_PREFIX.withPrefix(getName()))) {
+ pipeline.addLast(new TaipPrefixEncoder());
+ }
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new TaipProtocolDecoder(TaipProtocol.this));
@@ -42,7 +45,9 @@ public class TaipProtocol extends BaseProtocol {
addServer(new TrackerServer(config, getName(), true) {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
- pipeline.addLast(new TaipPrefixEncoder(TaipProtocol.this));
+ if (config.getBoolean(Keys.PROTOCOL_PREFIX.withPrefix(getName()))) {
+ pipeline.addLast(new TaipPrefixEncoder());
+ }
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new TaipProtocolDecoder(TaipProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/TaipProtocolDecoder.java b/src/main/java/org/traccar/protocol/TaipProtocolDecoder.java
index e5e84b7c4..448d7ffca 100644
--- a/src/main/java/org/traccar/protocol/TaipProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TaipProtocolDecoder.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.
@@ -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;
@@ -49,7 +48,7 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder {
.groupEnd("?")
.number("(d{5})") // seconds
.or()
- .expression("(?:RGP|RCQ|RCV|RBR|RUS00),?") // type
+ .expression("(?:RGP|RCQ|RCV|RBR|RUS00|RPI),?") // type
.number("(dd)?") // event
.number("(dd)(dd)(dd)") // date (mmddyy)
.number("(dd)(dd)(dd)") // time (hhmmss)
@@ -84,7 +83,7 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder {
.or()
.groupBegin()
.number("(xx)") // input
- .number("(xx)") // satellites
+ .number("xx") // satellites / outputs
.number("(ddd)") // battery
.number("(x{8})") // odometer
.number("[01]") // gps power
@@ -96,12 +95,14 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder {
.number("[01]") // modem power
.number("[0-5]") // gsm status
.number("(dd)") // rssi
+ .groupBegin()
.number("([-+]dddd)") // temperature 1
.number("xx") // seconds from last
.number("([-+]dddd)") // temperature 2
.number("xx") // seconds from last
.groupEnd("?")
.groupEnd("?")
+ .groupEnd("?")
.groupEnd()
.any()
.compile();
@@ -192,7 +193,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)) {
@@ -217,17 +218,18 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_HDOP, parser.nextInt());
}
- if (parser.hasNext(4)) {
+ if (parser.hasNext(3)) {
position.set(Position.KEY_INPUT, parser.nextHexInt(0));
- position.set(Position.KEY_SATELLITES, parser.nextHexInt(0));
position.set(Position.KEY_BATTERY, parser.nextInt(0));
position.set(Position.KEY_ODOMETER, parser.nextLong(16, 0));
}
- if (parser.hasNext(4)) {
+ if (parser.hasNext(3)) {
valid = parser.nextInt() > 0;
position.set(Position.KEY_PDOP, parser.nextInt());
position.set(Position.KEY_RSSI, parser.nextInt());
+ }
+ if (parser.hasNext(2)) {
position.set(Position.PREFIX_TEMP + 1, parser.nextInt() * 0.01);
position.set(Position.PREFIX_TEMP + 2, parser.nextInt() * 0.01);
}
@@ -262,6 +264,7 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder {
String uniqueId = null;
DeviceSession deviceSession = null;
String messageIndex = null;
+ boolean indexFirst = true;
if (attributes != null) {
for (String attribute : attributes) {
@@ -276,6 +279,9 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder {
if (deviceSession != null) {
position.setDeviceId(deviceSession.getDeviceId());
}
+ if (messageIndex == null) {
+ indexFirst = false;
+ }
break;
case "io":
position.set(Position.KEY_IGNITION, BitUtil.check(value.charAt(0) - '0', 0));
@@ -315,7 +321,11 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder {
if (messageIndex.startsWith("#IP")) {
response = ">SAK;ID=" + uniqueId + ";" + messageIndex + "<";
} else {
- response = ">ACK;ID=" + uniqueId + ";" + messageIndex + ";*";
+ if (indexFirst) {
+ response = ">ACK;" + messageIndex + ";ID=" + uniqueId + ";*";
+ } else {
+ response = ">ACK;ID=" + uniqueId + ";" + messageIndex + ";*";
+ }
response += String.format("%02X", Checksum.xor(response)) + "<";
}
channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
diff --git a/src/main/java/org/traccar/protocol/TechTltProtocol.java b/src/main/java/org/traccar/protocol/TechTltProtocol.java
index 191dd9ccc..a4a7460b0 100644
--- a/src/main/java/org/traccar/protocol/TechTltProtocol.java
+++ b/src/main/java/org/traccar/protocol/TechTltProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class TechTltProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/TechtoCruzProtocol.java b/src/main/java/org/traccar/protocol/TechtoCruzProtocol.java
index 265a3eb64..f0828a99e 100644
--- a/src/main/java/org/traccar/protocol/TechtoCruzProtocol.java
+++ b/src/main/java/org/traccar/protocol/TechtoCruzProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class TechtoCruzProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/TekProtocol.java b/src/main/java/org/traccar/protocol/TekProtocol.java
index 54e860d79..56714041b 100644
--- a/src/main/java/org/traccar/protocol/TekProtocol.java
+++ b/src/main/java/org/traccar/protocol/TekProtocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class TekProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/TelemaxProtocol.java b/src/main/java/org/traccar/protocol/TelemaxProtocol.java
index 9e9cbb50e..792a5b176 100644
--- a/src/main/java/org/traccar/protocol/TelemaxProtocol.java
+++ b/src/main/java/org/traccar/protocol/TelemaxProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class TelemaxProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/TelicProtocol.java b/src/main/java/org/traccar/protocol/TelicProtocol.java
index 9ef7864ca..fc5bdf0d1 100644
--- a/src/main/java/org/traccar/protocol/TelicProtocol.java
+++ b/src/main/java/org/traccar/protocol/TelicProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class TelicProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/TeltonikaFrameDecoder.java b/src/main/java/org/traccar/protocol/TeltonikaFrameDecoder.java
index 3a0962584..aabc24887 100644
--- a/src/main/java/org/traccar/protocol/TeltonikaFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/TeltonikaFrameDecoder.java
@@ -29,7 +29,7 @@ public class TeltonikaFrameDecoder extends BaseFrameDecoder {
protected Object decode(
ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
- while (buf.isReadable() && buf.getByte(buf.readerIndex()) == (byte) 0xff) {
+ if (buf.isReadable() && buf.getByte(buf.readerIndex()) == (byte) 0xff) {
return buf.readRetainedSlice(1);
}
diff --git a/src/main/java/org/traccar/protocol/TeltonikaProtocol.java b/src/main/java/org/traccar/protocol/TeltonikaProtocol.java
index 38283cb64..f2d610251 100644
--- a/src/main/java/org/traccar/protocol/TeltonikaProtocol.java
+++ b/src/main/java/org/traccar/protocol/TeltonikaProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class TeltonikaProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
index 929eca8aa..6197c6c13 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 - 2022 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,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.model.Device;
+import org.traccar.helper.BufferUtil;
import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
@@ -33,6 +33,7 @@ import org.traccar.model.Network;
import org.traccar.model.Position;
import java.net.SocketAddress;
+import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
@@ -112,18 +113,6 @@ 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, DeviceSession deviceSession, Position position, ByteBuf buf) {
@@ -169,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(";");
@@ -231,6 +220,8 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
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));
@@ -239,28 +230,52 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
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();
+ long driverUniqueId = b.readLongLE();
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(115, fmbXXX, (p, b) -> p.set(Position.KEY_COOLANT_TEMP, b.readShort() * 0.1));
+ 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("cid", b.readUnsignedShort()));
+ 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:
@@ -276,6 +291,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
break;
}
});
+ register(636, fmbXXX, (p, b) -> p.set("cid4g", b.readUnsignedInt()));
}
private void decodeGh3000Parameter(Position position, int id, ByteBuf buf, int length) {
@@ -366,14 +382,23 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
position.setNetwork(network);
}
} else {
- Integer cid = (Integer) position.getAttributes().remove("cid");
+ Integer cid2g = (Integer) position.getAttributes().remove("cid2g");
+ Long cid4g = (Long) position.getAttributes().remove("cid4g");
Integer lac = (Integer) position.getAttributes().remove("lac");
- if (cid != null && lac != null) {
- CellTower cellTower = CellTower.fromLacCid(getConfig(), lac, cid);
+ 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));
}
}
@@ -563,6 +588,38 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
index += 1;
}
+ } else if (id == 548 || id == 10829 || id == 10831) {
+ ByteBuf data = buf.readSlice(length);
+ data.readUnsignedByte(); // header
+ for (int i = 1; data.isReadable(); i++) {
+ ByteBuf beacon = data.readSlice(data.readUnsignedByte());
+ while (beacon.isReadable()) {
+ int parameterId = beacon.readUnsignedByte();
+ int parameterLength = beacon.readUnsignedByte();
+ switch (parameterId) {
+ case 0:
+ position.set("tag" + i + "Rssi", (int) beacon.readByte());
+ break;
+ case 1:
+ String beaconId = ByteBufUtil.hexDump(beacon.readSlice(parameterLength));
+ position.set("tag" + i + "Id", beaconId);
+ break;
+ case 2:
+ String beaconData = ByteBufUtil.hexDump(beacon.readSlice(parameterLength));
+ position.set("tag" + i + "Data", beaconData);
+ break;
+ case 13:
+ position.set("tag" + i + "LowBattery", beacon.readUnsignedByte());
+ break;
+ case 14:
+ position.set("tag" + i + "Battery", beacon.readUnsignedShort());
+ break;
+ default:
+ beacon.skipBytes(parameterLength);
+ break;
+ }
+ }
+ }
} else {
position.set(Position.PREFIX_IO + id, ByteBufUtil.hexDump(buf.readSlice(length)));
}
@@ -571,6 +628,14 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
decodeNetwork(position, model);
+ if (model != null && model.matches("FM.6..")) {
+ Long driverMsb = (Long) position.getAttributes().get("io195");
+ Long driverLsb = (Long) position.getAttributes().get("io196");
+ if (driverMsb != null && driverLsb != null) {
+ String driver = new String(ByteBuffer.allocate(16).putLong(driverMsb).putLong(driverLsb).array());
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, driver);
+ }
+ }
}
private List<Position> parseData(
@@ -588,7 +653,6 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
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());
@@ -600,9 +664,13 @@ 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)));
@@ -610,7 +678,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
} else if (codec == CODEC_12) {
decodeSerial(channel, remoteAddress, deviceSession, position, buf);
} else {
- decodeLocation(position, buf, codec, model);
+ decodeLocation(position, buf, codec, getDeviceModel(deviceSession));
}
if (!position.getOutdated() || !position.getAttributes().isEmpty()) {
diff --git a/src/main/java/org/traccar/protocol/TeraTrackProtocol.java b/src/main/java/org/traccar/protocol/TeraTrackProtocol.java
index 73219cc5e..e872ddf42 100644
--- a/src/main/java/org/traccar/protocol/TeraTrackProtocol.java
+++ b/src/main/java/org/traccar/protocol/TeraTrackProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class TeraTrackProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/TeraTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/TeraTrackProtocolDecoder.java
index 313210f63..be4b98e4c 100644
--- a/src/main/java/org/traccar/protocol/TeraTrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TeraTrackProtocolDecoder.java
@@ -23,8 +23,8 @@ 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 38bf078aa..b23dadf08 100644
--- a/src/main/java/org/traccar/protocol/ThinkPowerProtocol.java
+++ b/src/main/java/org/traccar/protocol/ThinkPowerProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class ThinkPowerProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/ThinkRaceProtocol.java b/src/main/java/org/traccar/protocol/ThinkRaceProtocol.java
index 782b0a352..34b80ba87 100644
--- a/src/main/java/org/traccar/protocol/ThinkRaceProtocol.java
+++ b/src/main/java/org/traccar/protocol/ThinkRaceProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class ThinkRaceProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/ThurayaProtocol.java b/src/main/java/org/traccar/protocol/ThurayaProtocol.java
index f709a1183..33d486f6b 100644
--- a/src/main/java/org/traccar/protocol/ThurayaProtocol.java
+++ b/src/main/java/org/traccar/protocol/ThurayaProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class ThurayaProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Tk102Protocol.java b/src/main/java/org/traccar/protocol/Tk102Protocol.java
index 150e83ab3..b6a82981b 100644
--- a/src/main/java/org/traccar/protocol/Tk102Protocol.java
+++ b/src/main/java/org/traccar/protocol/Tk102Protocol.java
@@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Tk102Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Tk103Protocol.java b/src/main/java/org/traccar/protocol/Tk103Protocol.java
index cf09886f5..b641ef083 100644
--- a/src/main/java/org/traccar/protocol/Tk103Protocol.java
+++ b/src/main/java/org/traccar/protocol/Tk103Protocol.java
@@ -24,7 +24,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Tk103Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java
index b343c3b33..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 - 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,8 +15,11 @@
*/
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.helper.DataConverter;
import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
@@ -448,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 {
@@ -477,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/Tlt2hProtocol.java b/src/main/java/org/traccar/protocol/Tlt2hProtocol.java
index b10271f7d..6763e9b6b 100644
--- a/src/main/java/org/traccar/protocol/Tlt2hProtocol.java
+++ b/src/main/java/org/traccar/protocol/Tlt2hProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Tlt2hProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java
index e85bdf9b3..6be3d2dc3 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 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2024 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -55,16 +55,20 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder {
private static final Pattern PATTERN_POSITION = new PatternBuilder()
.text("#")
- .number("(?:(dd)|x*)") // cell or voltage
+ .number("(?:(dd|dddd)|x*)") // cell or voltage
.groupBegin()
- .number("#(d+),") // mcc
+ .text("#")
+ .groupBegin()
+ .number("(d+),") // mcc
.number("(d+),") // mnc
.number("(x+),") // lac
.number("(x+)") // cell id
.groupEnd("?")
+ .groupEnd("?")
.text("$GPRMC,")
- .number("(dd)(dd)(dd).d+,") // time (hhmmss.sss)
+ .number("(?:(dd)(dd)(dd).d+)?,") // time (hhmmss.sss)
.expression("([AVL]),") // validity
+ .groupBegin()
.number("(d+)(dd.d+),") // latitude
.expression("([NS]),")
.number("(d+)(dd.d+),") // longitude
@@ -72,14 +76,16 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder {
.number("(d+.?d*)?,") // speed
.number("(d+.?d*)?,") // course
.number("(dd)(dd)(dd)") // date (ddmmyy)
+ .groupEnd("?")
.any()
.compile();
private static final Pattern PATTERN_WIFI = new PatternBuilder()
.text("#")
- .number("(?:(dd)|x+)") // cell or voltage
+ .number("(?:(dd|dddd)|x+)") // cell or voltage
+ .expression("#?")
.groupBegin()
- .number("#(d+),") // mcc
+ .number("(d+),") // mcc
.number("(d+),") // mnc
.number("(x+),") // lac
.number("(x+)") // cell id
@@ -178,7 +184,8 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder {
if (parser.matches()) {
if (parser.hasNext()) {
- position.set(Position.KEY_BATTERY, parser.nextInt() * 0.1);
+ int voltage = parser.nextInt();
+ position.set(Position.KEY_BATTERY, voltage > 100 ? voltage * 0.001 : voltage * 0.1);
}
if (parser.hasNext(4)) {
@@ -188,17 +195,26 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder {
position.setNetwork(network);
}
- DateBuilder dateBuilder = new DateBuilder()
- .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
+ DateBuilder dateBuilder = new DateBuilder();
+ if (parser.hasNext(3)) {
+ 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(0));
- position.setCourse(parser.nextDouble(0));
- dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
- position.setTime(dateBuilder.getDate());
+ if (parser.hasNext()) {
+
+ position.setLatitude(parser.nextCoordinate());
+ position.setLongitude(parser.nextCoordinate());
+ position.setSpeed(parser.nextDouble(0));
+ position.setCourse(parser.nextDouble(0));
+
+ dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
+ position.setTime(dateBuilder.getDate());
+
+ } else {
+ getLastLocation(position, null);
+ }
} else {
continue;
@@ -209,7 +225,10 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder {
parser = new Parser(PATTERN_WIFI, message);
if (parser.matches()) {
- position.set(Position.KEY_BATTERY, parser.nextInt() * 0.1);
+ if (parser.hasNext()) {
+ int voltage = parser.nextInt();
+ position.set(Position.KEY_BATTERY, voltage > 100 ? voltage * 0.001 : voltage * 0.1);
+ }
Network network = new Network();
if (parser.hasNext(4)) {
@@ -230,6 +249,8 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder {
dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
getLastLocation(position, dateBuilder.getDate());
+ } else {
+ continue;
}
} else {
diff --git a/src/main/java/org/traccar/protocol/TlvProtocol.java b/src/main/java/org/traccar/protocol/TlvProtocol.java
index 9d83388c9..f99676d23 100644
--- a/src/main/java/org/traccar/protocol/TlvProtocol.java
+++ b/src/main/java/org/traccar/protocol/TlvProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class TlvProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/TmgProtocol.java b/src/main/java/org/traccar/protocol/TmgProtocol.java
index e078c425b..dbba648be 100644
--- a/src/main/java/org/traccar/protocol/TmgProtocol.java
+++ b/src/main/java/org/traccar/protocol/TmgProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class TmgProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/TopflytechProtocol.java b/src/main/java/org/traccar/protocol/TopflytechProtocol.java
index 339d2fc8d..a658235ab 100644
--- a/src/main/java/org/traccar/protocol/TopflytechProtocol.java
+++ b/src/main/java/org/traccar/protocol/TopflytechProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class TopflytechProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/TopinProtocol.java b/src/main/java/org/traccar/protocol/TopinProtocol.java
index b15373d71..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.
@@ -19,16 +19,19 @@ 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 javax.inject.Inject;
+import jakarta.inject.Inject;
public class TopinProtocol extends BaseProtocol {
@Inject
public TopinProtocol(Config config) {
- setSupportedDataCommands(
- Command.TYPE_SOS_NUMBER);
+ 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, Config config) {
diff --git a/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java b/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java
index a1d5481db..c7b816818 100644
--- a/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java
@@ -48,7 +48,12 @@ 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_LBS_WIFI_2 = 0x1A;
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 +221,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_2 || 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 9ab36fd0b..b02d4f1fc 100644
--- a/src/main/java/org/traccar/protocol/TotemProtocol.java
+++ b/src/main/java/org/traccar/protocol/TotemProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class TotemProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/TotemProtocolDecoder.java b/src/main/java/org/traccar/protocol/TotemProtocolDecoder.java
index 9d0d794f8..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.
@@ -135,7 +135,7 @@ 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)
@@ -176,7 +176,21 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
.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
@@ -257,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)));
@@ -283,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());
@@ -331,10 +358,23 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
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)));
@@ -363,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();
@@ -427,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());
@@ -451,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
@@ -459,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));
@@ -511,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 615fdab28..3b3fc02b6 100644
--- a/src/main/java/org/traccar/protocol/Tr20Protocol.java
+++ b/src/main/java/org/traccar/protocol/Tr20Protocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Tr20Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Tr900Protocol.java b/src/main/java/org/traccar/protocol/Tr900Protocol.java
index 162cbe651..c5f357604 100644
--- a/src/main/java/org/traccar/protocol/Tr900Protocol.java
+++ b/src/main/java/org/traccar/protocol/Tr900Protocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Tr900Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/TrackboxProtocol.java b/src/main/java/org/traccar/protocol/TrackboxProtocol.java
index 4236144a3..eadcd07f9 100644
--- a/src/main/java/org/traccar/protocol/TrackboxProtocol.java
+++ b/src/main/java/org/traccar/protocol/TrackboxProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class TrackboxProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/TrakMateProtocol.java b/src/main/java/org/traccar/protocol/TrakMateProtocol.java
index b7637e6f3..f4e7c5e60 100644
--- a/src/main/java/org/traccar/protocol/TrakMateProtocol.java
+++ b/src/main/java/org/traccar/protocol/TrakMateProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class TrakMateProtocol extends BaseProtocol {
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 79a59abd3..5d8baf2a9 100644
--- a/src/main/java/org/traccar/protocol/TramigoProtocol.java
+++ b/src/main/java/org/traccar/protocol/TramigoProtocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class TramigoProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/TramigoProtocolDecoder.java b/src/main/java/org/traccar/protocol/TramigoProtocolDecoder.java
index 21dd78da3..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,6 +19,8 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.Checksum;
import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
@@ -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 e67afbda2..7bdf3d2d0 100644
--- a/src/main/java/org/traccar/protocol/TrvProtocol.java
+++ b/src/main/java/org/traccar/protocol/TrvProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class TrvProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java b/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java
index 9df29ae1b..02744f8ab 100644
--- a/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java
@@ -64,10 +64,20 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
.number("(d+),") // mnc
.number("(d+),") // lac
.number("(d+)") // cell
+ .groupBegin()
+ .text(",")
+ .expression("(")
+ .groupBegin()
+ .expression("[^\\|]+") // name
+ .number("|xx-xx-xx-xx-xx-xx") // mac
+ .number("|d+&?") // signal
+ .groupEnd("+")
+ .expression(")")
+ .groupEnd("?")
.any()
.compile();
- private static final Pattern PATTERN_HEATRBEAT = new PatternBuilder()
+ private static final Pattern PATTERN_HEARTBEAT = new PatternBuilder()
.expression("[A-Z]{2,3}")
.text("CP01,")
.number("(ddd)") // gsm
@@ -130,6 +140,16 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
}
}
+ private void decodeWifi(Network network, String data) {
+ for (String wifi : data.split("&")) {
+ if (!wifi.isEmpty()) {
+ String[] values = wifi.split("\\|");
+ network.addWifiAccessPoint(WifiAccessPoint.from(
+ values[1].replace('-', ':'), Integer.parseInt(values[2])));
+ }
+ }
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -163,7 +183,7 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
if (type.equals("CP01")) {
- Parser parser = new Parser(PATTERN_HEATRBEAT, sentence);
+ Parser parser = new Parser(PATTERN_HEARTBEAT, sentence);
if (!parser.matches()) {
return null;
}
@@ -208,8 +228,16 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
decodeCommon(position, parser);
- position.setNetwork(new Network(CellTower.from(
- parser.nextInt(), parser.nextInt(), parser.nextInt(), parser.nextInt())));
+ Network network = new Network();
+
+ network.addCellTower(CellTower.from(
+ parser.nextInt(), parser.nextInt(), parser.nextInt(), parser.nextInt()));
+
+ if (parser.hasNext()) {
+ decodeWifi(network, parser.next());
+ }
+
+ position.setNetwork(network);
return position;
@@ -241,12 +269,7 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
}
}
- for (String wifi : parser.next().split("&")) {
- if (!wifi.isEmpty()) {
- String[] values = wifi.split("\\|");
- network.addWifiAccessPoint(WifiAccessPoint.from(values[1], Integer.parseInt(values[2])));
- }
- }
+ decodeWifi(network, parser.next());
position.setNetwork(network);
diff --git a/src/main/java/org/traccar/protocol/Tt8850Protocol.java b/src/main/java/org/traccar/protocol/Tt8850Protocol.java
index ab109e274..8e2800d90 100644
--- a/src/main/java/org/traccar/protocol/Tt8850Protocol.java
+++ b/src/main/java/org/traccar/protocol/Tt8850Protocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Tt8850Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/TytanProtocol.java b/src/main/java/org/traccar/protocol/TytanProtocol.java
index cc3bc9b52..4fd3c807f 100644
--- a/src/main/java/org/traccar/protocol/TytanProtocol.java
+++ b/src/main/java/org/traccar/protocol/TytanProtocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class TytanProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/TzoneProtocol.java b/src/main/java/org/traccar/protocol/TzoneProtocol.java
index d25757b63..2df721049 100644
--- a/src/main/java/org/traccar/protocol/TzoneProtocol.java
+++ b/src/main/java/org/traccar/protocol/TzoneProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class TzoneProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java b/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java
index 8e84a6781..f0b1e709d 100644
--- a/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java
@@ -204,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);
}
}
@@ -364,9 +373,9 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder {
}
- if (hardware == 0x406) {
+ if (hardware == 0x153 || hardware == 0x406) {
- decodeTags(position, buf);
+ decodeTags(position, buf, hardware);
}
diff --git a/src/main/java/org/traccar/protocol/UlbotechProtocol.java b/src/main/java/org/traccar/protocol/UlbotechProtocol.java
index 57fc47644..f8c4f1960 100644
--- a/src/main/java/org/traccar/protocol/UlbotechProtocol.java
+++ b/src/main/java/org/traccar/protocol/UlbotechProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class UlbotechProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/UproProtocol.java b/src/main/java/org/traccar/protocol/UproProtocol.java
index e27088594..cbec9777d 100644
--- a/src/main/java/org/traccar/protocol/UproProtocol.java
+++ b/src/main/java/org/traccar/protocol/UproProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class UproProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/UproProtocolDecoder.java b/src/main/java/org/traccar/protocol/UproProtocolDecoder.java
index ed714e464..8d2e5de0a 100644
--- a/src/main/java/org/traccar/protocol/UproProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/UproProtocolDecoder.java
@@ -310,6 +310,10 @@ public class UproProtocolDecoder extends BaseProtocolDecoder {
position.set("serial", data.toString(StandardCharsets.US_ASCII).substring(3));
}
break;
+ case 'd':
+ position.set(Position.PREFIX_ADC + 1,
+ Integer.parseInt(data.toString(StandardCharsets.US_ASCII)) / 100.0);
+ break;
default:
break;
}
diff --git a/src/main/java/org/traccar/protocol/UuxProtocol.java b/src/main/java/org/traccar/protocol/UuxProtocol.java
index 3de4a4732..63727cb94 100644
--- a/src/main/java/org/traccar/protocol/UuxProtocol.java
+++ b/src/main/java/org/traccar/protocol/UuxProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class UuxProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/V680Protocol.java b/src/main/java/org/traccar/protocol/V680Protocol.java
index 53bca849c..587a0c8f7 100644
--- a/src/main/java/org/traccar/protocol/V680Protocol.java
+++ b/src/main/java/org/traccar/protocol/V680Protocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class V680Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/ValtrackProtocol.java b/src/main/java/org/traccar/protocol/ValtrackProtocol.java
new file mode 100644
index 000000000..8471a0fd8
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ValtrackProtocol.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2024 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.http.HttpObjectAggregator;
+import io.netty.handler.codec.http.HttpRequestDecoder;
+import io.netty.handler.codec.http.HttpResponseEncoder;
+import jakarta.inject.Inject;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+public class ValtrackProtocol extends BaseProtocol {
+
+ @Inject
+ public ValtrackProtocol(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 ValtrackProtocolDecoder(ValtrackProtocol.this));
+ }
+ });
+ }
+}
diff --git a/src/main/java/org/traccar/protocol/ValtrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/ValtrackProtocolDecoder.java
new file mode 100644
index 000000000..dc9ca8590
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ValtrackProtocolDecoder.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2024 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.channel.Channel;
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.HttpResponseStatus;
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import org.traccar.BaseHttpProtocolDecoder;
+import org.traccar.Protocol;
+import org.traccar.model.Position;
+import org.traccar.session.DeviceSession;
+
+import java.io.StringReader;
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+public class ValtrackProtocolDecoder extends BaseHttpProtocolDecoder {
+
+ public ValtrackProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ FullHttpRequest request = (FullHttpRequest) msg;
+ String content = request.content().toString(StandardCharsets.UTF_8);
+ JsonObject object = Json.createReader(new StringReader(content)).readObject();
+ JsonArray messages = object.getJsonArray("resource");
+
+ List<Position> positions = new LinkedList<>();
+ for (int i = 0; i < messages.size(); i++) {
+
+ JsonObject message = messages.getJsonObject(i);
+ String id = message.getString("devid");
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
+ if (deviceSession == null) {
+ continue;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setValid(true);
+ position.setTime(new Date());
+ position.setLatitude(Double.parseDouble(message.getString("lat")));
+ position.setLongitude(Double.parseDouble(message.getString("lon")));
+ String speed = message.getString("speed");
+ if (!speed.isEmpty()) {
+ position.setSpeed(Double.parseDouble(speed));
+ }
+
+ position.set(Position.KEY_BATTERY, Double.parseDouble(message.getString("vbat")));
+
+ positions.add(position);
+
+ }
+
+ sendResponse(channel, HttpResponseStatus.OK);
+ return positions;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/VisiontekProtocol.java b/src/main/java/org/traccar/protocol/VisiontekProtocol.java
index 5296402b4..83bcd37ff 100644
--- a/src/main/java/org/traccar/protocol/VisiontekProtocol.java
+++ b/src/main/java/org/traccar/protocol/VisiontekProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class VisiontekProtocol extends BaseProtocol {
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 dd739f0d9..6ccc54483 100644
--- a/src/main/java/org/traccar/protocol/VnetProtocol.java
+++ b/src/main/java/org/traccar/protocol/VnetProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.config.Config;
import java.nio.ByteOrder;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class VnetProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Vt200Protocol.java b/src/main/java/org/traccar/protocol/Vt200Protocol.java
index efb5fe2fd..97e64b74f 100644
--- a/src/main/java/org/traccar/protocol/Vt200Protocol.java
+++ b/src/main/java/org/traccar/protocol/Vt200Protocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Vt200Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Vt200ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Vt200ProtocolDecoder.java
index a8fc801e7..1ad15f39c 100644
--- a/src/main/java/org/traccar/protocol/Vt200ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Vt200ProtocolDecoder.java
@@ -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 482ab4a37..91453c413 100644
--- a/src/main/java/org/traccar/protocol/VtfmsProtocol.java
+++ b/src/main/java/org/traccar/protocol/VtfmsProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class VtfmsProtocol extends BaseProtocol {
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 600f81328..aee70b6ec 100644
--- a/src/main/java/org/traccar/protocol/WatchProtocol.java
+++ b/src/main/java/org/traccar/protocol/WatchProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class WatchProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java b/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java
index 40d56b130..b586f4e92 100644
--- a/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java
@@ -51,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
@@ -285,7 +285,8 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
|| type.equalsIgnoreCase("BLOOD")
|| type.equalsIgnoreCase("BPHRT")
|| type.equalsIgnoreCase("TEMP")
- || type.equalsIgnoreCase("btemp2")) {
+ || type.equalsIgnoreCase("btemp2")
+ || type.equalsIgnoreCase("oxygen")) {
if (buf.isReadable()) {
@@ -303,6 +304,8 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
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++]);
diff --git a/src/main/java/org/traccar/protocol/WialonProtocol.java b/src/main/java/org/traccar/protocol/WialonProtocol.java
index a744349cd..84033132d 100644
--- a/src/main/java/org/traccar/protocol/WialonProtocol.java
+++ b/src/main/java/org/traccar/protocol/WialonProtocol.java
@@ -27,7 +27,7 @@ import org.traccar.model.Command;
import java.nio.charset.StandardCharsets;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class WialonProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java b/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java
index 3d57525b7..4d1b34dba 100644
--- a/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java
@@ -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 f7084e55b..5b9ebb520 100644
--- a/src/main/java/org/traccar/protocol/WliProtocol.java
+++ b/src/main/java/org/traccar/protocol/WliProtocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class WliProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/WondexProtocol.java b/src/main/java/org/traccar/protocol/WondexProtocol.java
index 5a0401df4..e27b8e2bb 100644
--- a/src/main/java/org/traccar/protocol/WondexProtocol.java
+++ b/src/main/java/org/traccar/protocol/WondexProtocol.java
@@ -22,7 +22,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class WondexProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/WristbandProtocol.java b/src/main/java/org/traccar/protocol/WristbandProtocol.java
index c5d8d4050..117daf8cf 100644
--- a/src/main/java/org/traccar/protocol/WristbandProtocol.java
+++ b/src/main/java/org/traccar/protocol/WristbandProtocol.java
@@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class WristbandProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Xexun2Protocol.java b/src/main/java/org/traccar/protocol/Xexun2Protocol.java
index 52cf731f0..9dd517cfa 100644
--- a/src/main/java/org/traccar/protocol/Xexun2Protocol.java
+++ b/src/main/java/org/traccar/protocol/Xexun2Protocol.java
@@ -21,7 +21,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Xexun2Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Xexun2ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Xexun2ProtocolDecoder.java
index 913dfaf28..0e3c44e12 100644
--- a/src/main/java/org/traccar/protocol/Xexun2ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Xexun2ProtocolDecoder.java
@@ -156,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)) {
@@ -164,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) {
diff --git a/src/main/java/org/traccar/protocol/XexunProtocol.java b/src/main/java/org/traccar/protocol/XexunProtocol.java
index 5c7329603..e76e47d19 100644
--- a/src/main/java/org/traccar/protocol/XexunProtocol.java
+++ b/src/main/java/org/traccar/protocol/XexunProtocol.java
@@ -25,7 +25,7 @@ import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class XexunProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/XirgoProtocol.java b/src/main/java/org/traccar/protocol/XirgoProtocol.java
index 0841d86d5..7e14c6842 100644
--- a/src/main/java/org/traccar/protocol/XirgoProtocol.java
+++ b/src/main/java/org/traccar/protocol/XirgoProtocol.java
@@ -24,7 +24,7 @@ import org.traccar.TrackerServer;
import org.traccar.config.Config;
import org.traccar.model.Command;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class XirgoProtocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Xrb28Protocol.java b/src/main/java/org/traccar/protocol/Xrb28Protocol.java
index 65c2a1230..135fb0928 100644
--- a/src/main/java/org/traccar/protocol/Xrb28Protocol.java
+++ b/src/main/java/org/traccar/protocol/Xrb28Protocol.java
@@ -26,7 +26,7 @@ import org.traccar.model.Command;
import java.nio.charset.StandardCharsets;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Xrb28Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java
index 88f2d07e5..6033293c4 100644
--- a/src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 - 2024 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@ import org.traccar.model.Command;
import org.traccar.model.Position;
import java.net.SocketAddress;
+import java.util.Arrays;
import java.util.regex.Pattern;
public class Xrb28ProtocolDecoder extends BaseProtocolDecoder {
@@ -46,6 +47,7 @@ public class Xrb28ProtocolDecoder extends BaseProtocolDecoder {
.expression("....,")
.expression("..,") // vendor
.number("d{15},") // imei
+ .number("d{12},").optional() // time
.expression("..,") // type
.number("[01],") // reserved
.number("(dd)(dd)(dd).d+,") // time (hhmmss)
@@ -67,23 +69,44 @@ public class Xrb28ProtocolDecoder extends BaseProtocolDecoder {
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
String sentence = (String) msg;
+ String[] values = sentence.replaceAll("#$", "").split(",");
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, sentence.substring(9, 24));
+ int index = 0;
+ String header = values[index++];
+ String vendor = values[index++];
+
+ String imei = values[index++];
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
if (deviceSession == null) {
return null;
}
- String type = sentence.substring(25, 27);
+ String time;
+ if (values[index].length() == 12) {
+ time = values[index++];
+ } else {
+ time = null;
+ }
+
+ String type = values[index++];
if (channel != null) {
+ StringBuilder response = new StringBuilder("\u00ff\u00ff");
+ response.append(header.replaceAll("R$", "S")).append(',');
+ response.append(vendor).append(',');
+ response.append(imei).append(',');
+ if (time != null) {
+ response.append(time).append(',');
+ }
if (type.matches("L0|L1|W0|E1")) {
- channel.write(new NetworkMessage(
- "\u00ff\u00ff*SCOS" + sentence.substring(5, 27) + "#\n",
- remoteAddress));
+ response.append(type).append("#\n");
+ channel.write(new NetworkMessage(response.toString(), remoteAddress));
} else if (type.equals("R0") && pendingCommand != null) {
- String command = pendingCommand.equals(Command.TYPE_ALARM_ARM) ? "L1," : "L0,";
- channel.write(new NetworkMessage(
- "\u00ff\u00ff*SCOS" + sentence.substring(5, 25) + command + sentence.substring(30) + "\n",
- remoteAddress));
+ String command = pendingCommand.equals(Command.TYPE_ALARM_ARM) ? "L1" : "L0";
+ response.append(command);
+ String[] remaining = Arrays.copyOfRange(values, index, values.length);
+ response.append(String.join(",", remaining));
+ response.append("#\n");
+ channel.write(new NetworkMessage(response.toString(), remoteAddress));
pendingCommand = null;
}
}
@@ -95,11 +118,6 @@ public class Xrb28ProtocolDecoder extends BaseProtocolDecoder {
getLastLocation(position, null);
- String payload = sentence.substring(25, sentence.length() - 1);
-
- int index = 0;
- String[] values = payload.substring(3).split(",");
-
switch (type) {
case "Q0":
position.set(Position.KEY_BATTERY, Integer.parseInt(values[index++]) * 0.01);
@@ -146,7 +164,8 @@ public class Xrb28ProtocolDecoder extends BaseProtocolDecoder {
case "K0":
case "I0":
case "M0":
- position.set(Position.KEY_RESULT, payload);
+ String[] remaining = Arrays.copyOfRange(values, index, values.length);
+ position.set(Position.KEY_RESULT, String.join(",", remaining));
break;
default:
break;
diff --git a/src/main/java/org/traccar/protocol/Xt013Protocol.java b/src/main/java/org/traccar/protocol/Xt013Protocol.java
index 9e9087609..25809463a 100644
--- a/src/main/java/org/traccar/protocol/Xt013Protocol.java
+++ b/src/main/java/org/traccar/protocol/Xt013Protocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Xt013Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Xt2400Protocol.java b/src/main/java/org/traccar/protocol/Xt2400Protocol.java
index e200adb9f..1b7fc840b 100644
--- a/src/main/java/org/traccar/protocol/Xt2400Protocol.java
+++ b/src/main/java/org/traccar/protocol/Xt2400Protocol.java
@@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class Xt2400Protocol extends BaseProtocol {
diff --git a/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java
index edcb3f535..11f9e0654 100644
--- a/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java
@@ -177,7 +177,7 @@ public class Xt2400ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_POWER, buf.readUnsignedByte() * 0.1);
break;
case 0x57:
- position.set(Position.KEY_OBD_SPEED, UnitsConverter.knotsFromKph(buf.readUnsignedShort()));
+ position.set(Position.KEY_OBD_SPEED, buf.readUnsignedShort());
break;
case 0x65:
position.set(Position.KEY_VIN, buf.readSlice(17).toString(StandardCharsets.US_ASCII));
diff --git a/src/main/java/org/traccar/protocol/YwtProtocol.java b/src/main/java/org/traccar/protocol/YwtProtocol.java
index fb44e2360..27c71cfa8 100644
--- a/src/main/java/org/traccar/protocol/YwtProtocol.java
+++ b/src/main/java/org/traccar/protocol/YwtProtocol.java
@@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class YwtProtocol extends BaseProtocol {
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
index df55c470e..521dc120a 100644
--- a/src/main/java/org/traccar/reports/CsvExportProvider.java
+++ b/src/main/java/org/traccar/reports/CsvExportProvider.java
@@ -21,7 +21,7 @@ import org.traccar.model.Position;
import org.traccar.storage.Storage;
import org.traccar.storage.StorageException;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Date;
diff --git a/src/main/java/org/traccar/reports/DevicesReportProvider.java b/src/main/java/org/traccar/reports/DevicesReportProvider.java
new file mode 100644
index 000000000..7b4294d53
--- /dev/null
+++ b/src/main/java/org/traccar/reports/DevicesReportProvider.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2024 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.reports;
+
+import jakarta.inject.Inject;
+import org.jxls.util.JxlsHelper;
+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.Message;
+import org.traccar.model.User;
+import org.traccar.reports.common.ReportUtils;
+import org.traccar.reports.model.DeviceReportItem;
+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.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.Collection;
+import java.util.stream.Collectors;
+
+public class DevicesReportProvider {
+
+ private final Config config;
+ private final ReportUtils reportUtils;
+ private final Storage storage;
+
+ @Inject
+ public DevicesReportProvider(Config config, ReportUtils reportUtils, Storage storage) {
+ this.config = config;
+ this.reportUtils = reportUtils;
+ this.storage = storage;
+ }
+
+ public Collection<DeviceReportItem> getObjects(long userId) throws StorageException {
+
+ var positions = PositionUtil.getLatestPositions(storage, userId).stream()
+ .collect(Collectors.toMap(Message::getDeviceId, p -> p));
+
+ return storage.getObjects(Device.class, new Request(
+ new Columns.All(),
+ new Condition.Permission(User.class, userId, Device.class))).stream()
+ .map(device -> new DeviceReportItem(device, positions.get(device.getId())))
+ .collect(Collectors.toUnmodifiableList());
+ }
+
+ public void getExcel(OutputStream outputStream, long userId) throws StorageException, IOException {
+
+ File file = Paths.get(config.getString(Keys.TEMPLATES_ROOT), "export", "devices.xlsx").toFile();
+ try (InputStream inputStream = new FileInputStream(file)) {
+ var context = reportUtils.initializeContext(userId);
+ context.putVar("items", getObjects(userId));
+ JxlsHelper.getInstance().setUseFastFormulaProcessor(false)
+ .processTemplate(inputStream, outputStream, context);
+ }
+ }
+}
diff --git a/src/main/java/org/traccar/reports/EventsReportProvider.java b/src/main/java/org/traccar/reports/EventsReportProvider.java
index 30f55ba80..f252f28cc 100644
--- a/src/main/java/org/traccar/reports/EventsReportProvider.java
+++ b/src/main/java/org/traccar/reports/EventsReportProvider.java
@@ -19,6 +19,7 @@ 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;
@@ -34,7 +35,7 @@ import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Order;
import org.traccar.storage.query.Request;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -76,7 +77,7 @@ public class EventsReportProvider {
reportUtils.checkPeriodLimit(from, to);
ArrayList<Event> result = new ArrayList<>();
- for (Device device: reportUtils.getAccessibleDevices(userId, deviceIds, groupIds)) {
+ 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) {
@@ -104,7 +105,7 @@ public class EventsReportProvider {
HashMap<Long, String> geofenceNames = new HashMap<>();
HashMap<Long, String> maintenanceNames = new HashMap<>();
HashMap<Long, Position> positions = new HashMap<>();
- for (Device device: reportUtils.getAccessibleDevices(userId, deviceIds, groupIds)) {
+ 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();) {
diff --git a/src/main/java/org/traccar/reports/GpxExportProvider.java b/src/main/java/org/traccar/reports/GpxExportProvider.java
index ccbd97fc3..1c45b6416 100644
--- a/src/main/java/org/traccar/reports/GpxExportProvider.java
+++ b/src/main/java/org/traccar/reports/GpxExportProvider.java
@@ -24,7 +24,7 @@ import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Request;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Date;
diff --git a/src/main/java/org/traccar/reports/KmlExportProvider.java b/src/main/java/org/traccar/reports/KmlExportProvider.java
index 24fcfb8ab..24dca018c 100644
--- a/src/main/java/org/traccar/reports/KmlExportProvider.java
+++ b/src/main/java/org/traccar/reports/KmlExportProvider.java
@@ -23,7 +23,7 @@ import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Request;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
diff --git a/src/main/java/org/traccar/reports/RouteReportProvider.java b/src/main/java/org/traccar/reports/RouteReportProvider.java
index 3ee651619..d761fe1e5 100644
--- a/src/main/java/org/traccar/reports/RouteReportProvider.java
+++ b/src/main/java/org/traccar/reports/RouteReportProvider.java
@@ -19,6 +19,7 @@ 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;
@@ -31,7 +32,7 @@ import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Request;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -41,6 +42,8 @@ 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 {
@@ -48,6 +51,8 @@ public class RouteReportProvider {
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;
@@ -60,12 +65,18 @@ public class RouteReportProvider {
reportUtils.checkPeriodLimit(from, to);
ArrayList<Position> result = new ArrayList<>();
- for (Device device: reportUtils.getAccessibleDevices(userId, deviceIds, groupIds)) {
+ 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 {
@@ -73,11 +84,11 @@ public class RouteReportProvider {
ArrayList<DeviceReportSection> devicesRoutes = new ArrayList<>();
ArrayList<String> sheetNames = new ArrayList<>();
- for (Device device: reportUtils.getAccessibleDevices(userId, deviceIds, groupIds)) {
+ 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(deviceRoutes.getDeviceName()));
+ 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())));
diff --git a/src/main/java/org/traccar/reports/StopsReportProvider.java b/src/main/java/org/traccar/reports/StopsReportProvider.java
index ec3fd2215..2160fec0e 100644
--- a/src/main/java/org/traccar/reports/StopsReportProvider.java
+++ b/src/main/java/org/traccar/reports/StopsReportProvider.java
@@ -19,7 +19,7 @@ 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.PositionUtil;
+import org.traccar.helper.model.DeviceUtil;
import org.traccar.model.Device;
import org.traccar.model.Group;
import org.traccar.reports.common.ReportUtils;
@@ -31,7 +31,7 @@ import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Request;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -55,20 +55,14 @@ public class StopsReportProvider {
this.storage = storage;
}
- private Collection<StopReportItem> detectStops(Device device, Date from, Date to) throws StorageException {
- boolean ignoreOdometer = config.getBoolean(Keys.REPORT_IGNORE_ODOMETER);
- var positions = PositionUtil.getPositions(storage, device.getId(), from, to);
- return reportUtils.detectTripsAndStops(device, positions, ignoreOdometer, StopReportItem.class);
- }
-
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: reportUtils.getAccessibleDevices(userId, deviceIds, groupIds)) {
- result.addAll(detectStops(device, from, to));
+ for (Device device: DeviceUtil.getAccessibleDevices(storage, userId, deviceIds, groupIds)) {
+ result.addAll(reportUtils.detectTripsAndStops(device, from, to, StopReportItem.class));
}
return result;
}
@@ -80,8 +74,8 @@ public class StopsReportProvider {
ArrayList<DeviceReportSection> devicesStops = new ArrayList<>();
ArrayList<String> sheetNames = new ArrayList<>();
- for (Device device: reportUtils.getAccessibleDevices(userId, deviceIds, groupIds)) {
- Collection<StopReportItem> stops = detectStops(device, from, to);
+ 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()));
diff --git a/src/main/java/org/traccar/reports/SummaryReportProvider.java b/src/main/java/org/traccar/reports/SummaryReportProvider.java
index 9415f3e81..ffde0b067 100644
--- a/src/main/java/org/traccar/reports/SummaryReportProvider.java
+++ b/src/main/java/org/traccar/reports/SummaryReportProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
+ * 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");
@@ -21,6 +21,7 @@ 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;
@@ -29,18 +30,25 @@ 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 javax.inject.Inject;
+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.Calendar;
import java.util.Collection;
import java.util.Date;
+import java.util.List;
public class SummaryReportProvider {
@@ -58,96 +66,105 @@ public class SummaryReportProvider {
this.storage = storage;
}
- private SummaryReportItem calculateSummaryResult(Device device, Collection<Position> positions) {
+ 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());
- if (positions != null && !positions.isEmpty()) {
- Position firstPosition = null;
- Position previousPosition = null;
+
+ 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 (firstPosition == null) {
- firstPosition = position;
+ if (first == null) {
+ first = position;
}
- previousPosition = 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(firstPosition, previousPosition, !ignoreOdometer));
- result.setSpentFuel(reportUtils.calculateFuel(firstPosition, previousPosition));
+ result.setDistance(PositionUtil.calculateDistance(first, last, !ignoreOdometer));
+ result.setSpentFuel(reportUtils.calculateFuel(first, last));
long durationMilliseconds;
- if (firstPosition.hasAttribute(Position.KEY_HOURS) && previousPosition.hasAttribute(Position.KEY_HOURS)) {
- durationMilliseconds =
- previousPosition.getLong(Position.KEY_HOURS) - firstPosition.getLong(Position.KEY_HOURS);
+ 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 =
- previousPosition.getFixTime().getTime() - firstPosition.getFixTime().getTime();
+ durationMilliseconds = last.getFixTime().getTime() - first.getFixTime().getTime();
}
if (durationMilliseconds > 0) {
- result.setAverageSpeed(
- UnitsConverter.knotsFromMps(result.getDistance() * 1000 / durationMilliseconds));
+ 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));
+ && 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(firstPosition.getDouble(Position.KEY_TOTAL_DISTANCE));
- result.setEndOdometer(previousPosition.getDouble(Position.KEY_TOTAL_DISTANCE));
+ result.setStartOdometer(first.getDouble(Position.KEY_TOTAL_DISTANCE));
+ result.setEndOdometer(last.getDouble(Position.KEY_TOTAL_DISTANCE));
}
- result.setStartTime(firstPosition.getFixTime());
- result.setEndTime(previousPosition.getFixTime());
+ result.setStartTime(first.getFixTime());
+ result.setEndTime(last.getFixTime());
+ return List.of(result);
}
- return result;
- }
- private int getDay(long userId, Date date) throws StorageException {
- Calendar calendar = Calendar.getInstance(UserUtil.getTimezone(
- permissionsService.getServer(), permissionsService.getUser(userId)));
- calendar.setTime(date);
- return calendar.get(Calendar.DAY_OF_MONTH);
+ return List.of();
}
- private Collection<SummaryReportItem> calculateSummaryResults(
- long userId, Device device, Date from, Date to, boolean daily) throws StorageException {
+ private Collection<SummaryReportItem> calculateDeviceResults(
+ Device device, ZonedDateTime from, ZonedDateTime to, boolean daily) throws StorageException {
- var positions = PositionUtil.getPositions(storage, device.getId(), from, to);
+ boolean fast = Duration.between(from, to).toSeconds() > config.getLong(Keys.REPORT_FAST_THRESHOLD);
var results = new ArrayList<SummaryReportItem>();
- 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(device, positions.subList(startIndex, i)));
- startIndex = i;
- startDay = currentDay;
- }
+ 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.add(calculateSummaryResult(device, positions.subList(startIndex, positions.size())));
+ results.addAll(calculateDeviceResult(device, Date.from(from.toInstant()), Date.from(to.toInstant()), fast));
} else {
- results.add(calculateSummaryResult(device, positions));
+ 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 {
+ 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: reportUtils.getAccessibleDevices(userId, deviceIds, groupIds)) {
- Collection<SummaryReportItem> deviceResults = calculateSummaryResults(userId, device, from, to, daily);
+ 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);
diff --git a/src/main/java/org/traccar/reports/TripsReportProvider.java b/src/main/java/org/traccar/reports/TripsReportProvider.java
index 265811354..9ff7232af 100644
--- a/src/main/java/org/traccar/reports/TripsReportProvider.java
+++ b/src/main/java/org/traccar/reports/TripsReportProvider.java
@@ -19,7 +19,7 @@ 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.PositionUtil;
+import org.traccar.helper.model.DeviceUtil;
import org.traccar.model.Device;
import org.traccar.model.Group;
import org.traccar.reports.common.ReportUtils;
@@ -31,7 +31,7 @@ import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Request;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -55,20 +55,14 @@ public class TripsReportProvider {
this.storage = storage;
}
- private Collection<TripReportItem> detectTrips(Device device, Date from, Date to) throws StorageException {
- boolean ignoreOdometer = config.getBoolean(Keys.REPORT_IGNORE_ODOMETER);
- var positions = PositionUtil.getPositions(storage, device.getId(), from, to);
- return reportUtils.detectTripsAndStops(device, positions, ignoreOdometer, TripReportItem.class);
- }
-
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: reportUtils.getAccessibleDevices(userId, deviceIds, groupIds)) {
- result.addAll(detectTrips(device, from, to));
+ for (Device device: DeviceUtil.getAccessibleDevices(storage, userId, deviceIds, groupIds)) {
+ result.addAll(reportUtils.detectTripsAndStops(device, from, to, TripReportItem.class));
}
return result;
}
@@ -80,8 +74,8 @@ public class TripsReportProvider {
ArrayList<DeviceReportSection> devicesTrips = new ArrayList<>();
ArrayList<String> sheetNames = new ArrayList<>();
- for (Device device: reportUtils.getAccessibleDevices(userId, deviceIds, groupIds)) {
- Collection<TripReportItem> trips = detectTrips(device, from, to);
+ 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()));
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/ReportMailer.java b/src/main/java/org/traccar/reports/common/ReportMailer.java
index 221b35ae1..9fb30fe9f 100644
--- a/src/main/java/org/traccar/reports/common/ReportMailer.java
+++ b/src/main/java/org/traccar/reports/common/ReportMailer.java
@@ -22,11 +22,11 @@ import org.traccar.mail.MailManager;
import org.traccar.model.User;
import org.traccar.storage.StorageException;
-import javax.activation.DataHandler;
-import javax.inject.Inject;
-import javax.mail.MessagingException;
-import javax.mail.internet.MimeBodyPart;
-import javax.mail.util.ByteArrayDataSource;
+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;
@@ -55,7 +55,7 @@ public class ReportMailer {
stream.toByteArray(), "application/octet-stream")));
User user = permissionsService.getUser(userId);
- mailManager.sendMessage(user, "Report", "The report is in the attachment.", attachment);
+ mailManager.sendMessage(user, false, "Report", "The report is in the attachment.", attachment);
} catch (StorageException | IOException | MessagingException e) {
LOGGER.warn("Email report failed", e);
}
diff --git a/src/main/java/org/traccar/reports/common/ReportUtils.java b/src/main/java/org/traccar/reports/common/ReportUtils.java
index a7c420095..3b8e84887 100644
--- a/src/main/java/org/traccar/reports/common/ReportUtils.java
+++ b/src/main/java/org/traccar/reports/common/ReportUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2023 Anton Tananaev (anton@traccar.org)
* Copyright 2016 - 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,6 +16,8 @@
*/
package org.traccar.reports.common;
+import jakarta.annotation.Nullable;
+import jakarta.inject.Inject;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.tools.generic.DateTool;
import org.apache.velocity.tools.generic.NumberTool;
@@ -31,12 +33,13 @@ 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.Group;
+import org.traccar.model.Event;
import org.traccar.model.Position;
import org.traccar.model.User;
import org.traccar.reports.model.BaseReportItem;
@@ -48,44 +51,35 @@ 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 javax.annotation.Nullable;
-import javax.inject.Inject;
-import javax.inject.Singleton;
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.Collection;
-import java.util.Collections;
import java.util.Date;
-import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
-import java.util.Objects;
+import java.util.Set;
import java.util.stream.Collectors;
-@Singleton
public class ReportUtils {
private final Config config;
private final Storage storage;
private final PermissionsService permissionsService;
- private final TripsConfig tripsConfig;
private final VelocityEngine velocityEngine;
private final Geocoder geocoder;
@Inject
public ReportUtils(
Config config, Storage storage, PermissionsService permissionsService,
- TripsConfig tripsConfig, VelocityEngine velocityEngine, @Nullable Geocoder geocoder) {
+ VelocityEngine velocityEngine, @Nullable Geocoder geocoder) {
this.config = config;
this.storage = storage;
this.permissionsService = permissionsService;
- this.tripsConfig = tripsConfig;
this.velocityEngine = velocityEngine;
this.geocoder = geocoder;
}
@@ -106,49 +100,11 @@ public class ReportUtils {
}
}
- public Collection<Device> getAccessibleDevices(
- 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;
- }
-
- 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();
+ public double calculateFuel(Position first, Position last) {
+ if (first.hasAttribute(Position.KEY_FUEL_USED) && last.hasAttribute(Position.KEY_FUEL_USED)) {
+ return last.getDouble(Position.KEY_FUEL_USED) - first.getDouble(Position.KEY_FUEL_USED);
+ } else if (first.hasAttribute(Position.KEY_FUEL_LEVEL) && last.hasAttribute(Position.KEY_FUEL_LEVEL)) {
+ return first.getDouble(Position.KEY_FUEL_LEVEL) - last.getDouble(Position.KEY_FUEL_LEVEL);
}
return 0;
}
@@ -205,20 +161,9 @@ public class ReportUtils {
}
private TripReportItem calculateTrip(
- Device device, ArrayList<Position> positions, int startIndex, int endIndex,
+ Device device, Position startTrip, Position endTrip, double maxSpeed,
boolean ignoreOdometer) throws StorageException {
- 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;
- }
- }
-
TripReportItem trip = new TripReportItem();
long tripDuration = endTrip.getFixTime().getTime() - startTrip.getFixTime().getTime();
@@ -251,7 +196,7 @@ public class ReportUtils {
if (tripDuration > 0) {
trip.setAverageSpeed(UnitsConverter.knotsFromMps(trip.getDistance() * 1000 / tripDuration));
}
- trip.setMaxSpeed(speedMax);
+ trip.setMaxSpeed(maxSpeed);
trip.setSpentFuel(calculateFuel(startTrip, endTrip));
trip.setDriverUniqueId(findDriver(startTrip, endTrip));
@@ -271,10 +216,7 @@ public class ReportUtils {
}
private StopReportItem calculateStop(
- Device device, ArrayList<Position> positions, int startIndex, int endIndex, boolean ignoreOdometer) {
-
- Position startStop = positions.get(startIndex);
- Position endStop = positions.get(endIndex);
+ Device device, Position startStop, Position endStop, boolean ignoreOdometer) {
StopReportItem stop = new StopReportItem();
@@ -318,17 +260,17 @@ public class ReportUtils {
@SuppressWarnings("unchecked")
private <T extends BaseReportItem> T calculateTripOrStop(
- Device device, ArrayList<Position> positions, int startIndex, int endIndex,
+ Device device, Position startPosition, Position endPosition, double maxSpeed,
boolean ignoreOdometer, Class<T> reportClass) throws StorageException {
if (reportClass.equals(TripReportItem.class)) {
- return (T) calculateTrip(device, positions, startIndex, endIndex, ignoreOdometer);
+ return (T) calculateTrip(device, startPosition, endPosition, maxSpeed, ignoreOdometer);
} else {
- return (T) calculateStop(device, positions, startIndex, endIndex, ignoreOdometer);
+ return (T) calculateStop(device, startPosition, endPosition, ignoreOdometer);
}
}
- private boolean isMoving(ArrayList<Position> positions, int index, TripsConfig tripsConfig) {
+ 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()
@@ -343,13 +285,26 @@ public class ReportUtils {
return positions.get(index).getBoolean(Position.KEY_MOTION);
}
- public <T extends BaseReportItem> Collection<T> detectTripsAndStops(
- Device device, Collection<Position> positionCollection, boolean ignoreOdometer,
- Class<T> reportClass) throws StorageException {
+ 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 {
- Collection<T> result = new ArrayList<>();
+ List<T> result = new ArrayList<>();
+ TripsConfig tripsConfig = new TripsConfig(
+ new AttributeUtil.StorageProvider(config, storage, permissionsService, device));
+ boolean ignoreOdometer = config.getBoolean(Keys.REPORT_IGNORE_ODOMETER);
- ArrayList<Position> positions = new ArrayList<>(positionCollection);
+ var positions = PositionUtil.getPositions(storage, device.getId(), from, to);
if (!positions.isEmpty()) {
boolean trips = reportClass.equals(TripReportItem.class);
@@ -359,17 +314,23 @@ public class ReportUtils {
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) {
- startEventIndex = detected ? startEventIndex : i;
+ 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);
@@ -379,17 +340,58 @@ public class ReportUtils {
startNoEventIndex = -1;
} else if (startEventIndex >= 0 && startNoEventIndex >= 0) {
result.add(calculateTripOrStop(
- device, positions, startEventIndex, startNoEventIndex, ignoreOdometer, reportClass));
+ device, positions.get(startEventIndex), positions.get(startNoEventIndex),
+ maxSpeed, ignoreOdometer, reportClass));
detected = false;
startEventIndex = -1;
startNoEventIndex = -1;
}
}
}
- if (startEventIndex >= 0 && startEventIndex < positions.size() - 1) {
+ if (detected & startEventIndex >= 0 && startEventIndex < positions.size() - 1) {
int endIndex = startNoEventIndex >= 0 ? startNoEventIndex : positions.size() - 1;
result.add(calculateTripOrStop(
- device, positions, startEventIndex, endIndex, ignoreOdometer, reportClass));
+ 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;
}
}
diff --git a/src/main/java/org/traccar/reports/common/TripsConfig.java b/src/main/java/org/traccar/reports/common/TripsConfig.java
index 52db97b74..2792114d4 100644
--- a/src/main/java/org/traccar/reports/common/TripsConfig.java
+++ b/src/main/java/org/traccar/reports/common/TripsConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2022 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");
@@ -16,37 +16,28 @@
*/
package org.traccar.reports.common;
-import org.traccar.config.Config;
import org.traccar.config.Keys;
+import org.traccar.helper.model.AttributeUtil;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-@Singleton
public class TripsConfig {
- @Inject
- public TripsConfig(Config config) {
+ public TripsConfig(AttributeUtil.Provider attributeProvider) {
this(
- 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));
+ 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, boolean processInvalidPositions, double speedThreshold) {
+ long minimalNoDataDuration, boolean useIgnition) {
this.minimalTripDistance = minimalTripDistance;
this.minimalTripDuration = minimalTripDuration;
this.minimalParkingDuration = minimalParkingDuration;
this.minimalNoDataDuration = minimalNoDataDuration;
this.useIgnition = useIgnition;
- this.processInvalidPositions = processInvalidPositions;
- this.speedThreshold = speedThreshold;
}
private final double minimalTripDistance;
@@ -79,16 +70,4 @@ public class TripsConfig {
return useIgnition;
}
- private final boolean processInvalidPositions;
-
- public boolean getProcessInvalidPositions() {
- return processInvalidPositions;
- }
-
- private final double speedThreshold;
-
- public double getSpeedThreshold() {
- return speedThreshold;
- }
-
}
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/DeviceReportItem.java b/src/main/java/org/traccar/reports/model/DeviceReportItem.java
new file mode 100644
index 000000000..74cd5f32c
--- /dev/null
+++ b/src/main/java/org/traccar/reports/model/DeviceReportItem.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2024 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.reports.model;
+
+import org.traccar.model.Device;
+import org.traccar.model.Position;
+
+public class DeviceReportItem {
+
+ public DeviceReportItem(Device device, Position position) {
+ this.device = device;
+ this.position = position;
+ }
+
+ 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;
+ }
+
+}
diff --git a/src/main/java/org/traccar/schedule/ScheduleManager.java b/src/main/java/org/traccar/schedule/ScheduleManager.java
index e1de3b3af..742428fd8 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 - 2023 Anton Tananaev (anton@traccar.org)
+ * Copyright 2020 - 2024 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,11 +18,11 @@ package org.traccar.schedule;
import com.google.inject.Injector;
import org.traccar.LifecycleObject;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-import java.util.List;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
+import java.util.stream.Stream;
@Singleton
public class ScheduleManager implements LifecycleObject {
@@ -38,12 +38,15 @@ public class ScheduleManager implements LifecycleObject {
@Override
public void start() {
executor = Executors.newSingleThreadScheduledExecutor();
- var tasks = List.of(
+ Stream.of(
+ TaskHealthCheck.class,
+ TaskClearStatus.class,
+ TaskExpirations.class,
+ TaskDeleteTemporary.class,
TaskReports.class,
TaskDeviceInactivityCheck.class,
- TaskWebSocketKeepalive.class,
- TaskHealthCheck.class);
- tasks.forEach(task -> injector.getInstance(task).schedule(executor));
+ TaskWebSocketKeepalive.class)
+ .forEachOrdered(task -> injector.getInstance(task).schedule(executor));
}
@Override
diff --git a/src/main/java/org/traccar/schedule/TaskClearStatus.java b/src/main/java/org/traccar/schedule/TaskClearStatus.java
new file mode 100644
index 000000000..78fecc0ea
--- /dev/null
+++ b/src/main/java/org/traccar/schedule/TaskClearStatus.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2024 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.schedule;
+
+import jakarta.inject.Inject;
+import org.traccar.broadcast.BroadcastService;
+import org.traccar.helper.model.DeviceUtil;
+import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
+
+import java.util.concurrent.ScheduledExecutorService;
+
+public class TaskClearStatus implements ScheduleTask {
+
+ @Inject
+ public TaskClearStatus(BroadcastService broadcastService, Storage storage) throws StorageException {
+ if (broadcastService.singleInstance()) {
+ DeviceUtil.resetStatus(storage);
+ }
+ }
+
+ @Override
+ public void schedule(ScheduledExecutorService executor) {
+ }
+
+ @Override
+ public void run() {
+ }
+
+}
diff --git a/src/main/java/org/traccar/schedule/TaskDeleteTemporary.java b/src/main/java/org/traccar/schedule/TaskDeleteTemporary.java
new file mode 100644
index 000000000..0cead59fb
--- /dev/null
+++ b/src/main/java/org/traccar/schedule/TaskDeleteTemporary.java
@@ -0,0 +1,61 @@
+/*
+ * 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 jakarta.inject.Inject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.model.User;
+import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Request;
+
+import java.util.Date;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+public class TaskDeleteTemporary implements ScheduleTask {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TaskDeleteTemporary.class);
+
+ private static final long CHECK_PERIOD_HOURS = 1;
+
+ private final Storage storage;
+
+ @Inject
+ public TaskDeleteTemporary(Storage storage) {
+ this.storage = storage;
+ }
+
+ @Override
+ public void schedule(ScheduledExecutorService executor) {
+ executor.scheduleAtFixedRate(this, CHECK_PERIOD_HOURS, CHECK_PERIOD_HOURS, TimeUnit.HOURS);
+ }
+
+ @Override
+ public void run() {
+ try {
+ storage.removeObject(User.class, new Request(
+ new Condition.And(
+ new Condition.Equals("temporary", true),
+ new Condition.Compare("expirationTime", "<", "time", new Date()))));
+ } catch (StorageException e) {
+ LOGGER.warn("Failed to delete temporary users", e);
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/schedule/TaskDeviceInactivityCheck.java b/src/main/java/org/traccar/schedule/TaskDeviceInactivityCheck.java
index 57c64dc5b..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 - 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.
@@ -20,17 +20,19 @@ 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 javax.inject.Inject;
+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 ScheduleTask {
@@ -64,22 +66,45 @@ public class TaskDeviceInactivityCheck implements ScheduleTask {
Map<Event, Position> events = new HashMap<>();
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, currentTime, checkPeriod)) {
+ 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("Get devices error", e);
+ LOGGER.warn("Database error", e);
}
notificationManager.updateEvents(events);
}
- private boolean checkDevice(Device device, long currentTime, long checkPeriod) {
- long deviceInactivityStart = device.getLong(ATTRIBUTE_DEVICE_INACTIVITY_START);
+ 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, 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) {
@@ -88,7 +113,7 @@ public class TaskDeviceInactivityCheck implements ScheduleTask {
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/TaskExpirations.java b/src/main/java/org/traccar/schedule/TaskExpirations.java
new file mode 100644
index 000000000..94f855c5f
--- /dev/null
+++ b/src/main/java/org/traccar/schedule/TaskExpirations.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2024 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.schedule;
+
+import jakarta.inject.Inject;
+import jakarta.mail.MessagingException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.mail.MailManager;
+import org.traccar.model.Device;
+import org.traccar.model.Disableable;
+import org.traccar.model.Server;
+import org.traccar.model.User;
+import org.traccar.notification.TextTemplateFormatter;
+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.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+public class TaskExpirations implements ScheduleTask {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TaskExpirations.class);
+
+ private static final long CHECK_PERIOD_HOURS = 1;
+
+ private final Config config;
+ private final Storage storage;
+ private final TextTemplateFormatter textTemplateFormatter;
+ private final MailManager mailManager;
+
+ @Inject
+ public TaskExpirations(
+ Config config, Storage storage, TextTemplateFormatter textTemplateFormatter, MailManager mailManager) {
+ this.config = config;
+ this.storage = storage;
+ this.textTemplateFormatter = textTemplateFormatter;
+ this.mailManager = mailManager;
+ }
+
+ @Override
+ public void schedule(ScheduledExecutorService executor) {
+ executor.scheduleAtFixedRate(this, CHECK_PERIOD_HOURS, CHECK_PERIOD_HOURS, TimeUnit.HOURS);
+ }
+
+ private boolean checkTimeTrigger(Disableable disableable, long currentTime, long offsetTime) {
+ if (disableable.getExpirationTime() != null) {
+ long previousTime = currentTime - TimeUnit.HOURS.toMillis(CHECK_PERIOD_HOURS);
+ long expirationTime = disableable.getExpirationTime().getTime() + offsetTime;
+ return previousTime < expirationTime && currentTime >= expirationTime;
+ }
+ return false;
+ }
+
+ private void sendUserExpiration(
+ Server server, User user, String template) throws MessagingException {
+ var velocityContext = textTemplateFormatter.prepareContext(server, user);
+ velocityContext.put("expiration", user.getExpirationTime());
+ var fullMessage = textTemplateFormatter.formatMessage(velocityContext, template, "full");
+ mailManager.sendMessage(user, true, fullMessage.getSubject(), fullMessage.getBody());
+ }
+
+ private void sendDeviceExpiration(
+ Server server, Device device, String template) throws MessagingException, StorageException {
+ var users = storage.getObjects(User.class, new Request(
+ new Columns.All(), new Condition.Permission(User.class, Device.class, device.getId())));
+ for (User user : users) {
+ var velocityContext = textTemplateFormatter.prepareContext(server, user);
+ velocityContext.put("expiration", device.getExpirationTime());
+ velocityContext.put("device", device);
+ var fullMessage = textTemplateFormatter.formatMessage(velocityContext, template, "full");
+ mailManager.sendMessage(user, true, fullMessage.getSubject(), fullMessage.getBody());
+ }
+ }
+
+ @Override
+ public void run() {
+ try {
+
+ long currentTime = System.currentTimeMillis();
+ Server server = storage.getObject(Server.class, new Request(new Columns.All()));
+
+ if (config.getBoolean(Keys.NOTIFICATION_EXPIRATION_USER)) {
+ long reminder = config.getLong(Keys.NOTIFICATION_EXPIRATION_USER_REMINDER);
+ var users = storage.getObjects(User.class, new Request(new Columns.All()));
+ for (User user : users) {
+ if (checkTimeTrigger(user, currentTime, 0)) {
+ sendUserExpiration(server, user, "userExpiration");
+ } else if (reminder > 0 && checkTimeTrigger(user, currentTime, -reminder)) {
+ sendUserExpiration(server, user, "userExpirationReminder");
+ }
+ }
+ }
+
+ if (config.getBoolean(Keys.NOTIFICATION_EXPIRATION_DEVICE)) {
+ long reminder = config.getLong(Keys.NOTIFICATION_EXPIRATION_USER_REMINDER);
+ var devices = storage.getObjects(Device.class, new Request(new Columns.All()));
+ for (Device device : devices) {
+ if (checkTimeTrigger(device, currentTime, 0)) {
+ sendDeviceExpiration(server, device, "deviceExpiration");
+ } else if (reminder > 0 && checkTimeTrigger(device, currentTime, -reminder)) {
+ sendDeviceExpiration(server, device, "deviceExpirationReminder");
+ }
+ }
+ }
+
+ } catch (StorageException | MessagingException e) {
+ LOGGER.warn("Failed to check expirations", e);
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/schedule/TaskHealthCheck.java b/src/main/java/org/traccar/schedule/TaskHealthCheck.java
index a8c9873ce..a60935f18 100644
--- a/src/main/java/org/traccar/schedule/TaskHealthCheck.java
+++ b/src/main/java/org/traccar/schedule/TaskHealthCheck.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2020 - 2024 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,8 +22,8 @@ import org.slf4j.LoggerFactory;
import org.traccar.config.Config;
import org.traccar.config.Keys;
-import javax.inject.Inject;
-import javax.ws.rs.client.Client;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.client.Client;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@@ -34,6 +34,8 @@ public class TaskHealthCheck implements ScheduleTask {
private final Config config;
private final Client client;
+ private final long gracePeriod = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(1);
+
private SystemD systemD;
private boolean enabled;
@@ -77,14 +79,22 @@ public class TaskHealthCheck implements ScheduleTask {
@Override
public void run() {
LOGGER.debug("Health check running");
- int status = client.target(getUrl()).request().get().getStatus();
- if (status == 200) {
- int result = systemD.sd_notify(0, "WATCHDOG=1");
- if (result < 0) {
- LOGGER.warn("Health check notify error {}", result);
+ if (System.currentTimeMillis() > gracePeriod) {
+ int status = client.target(getUrl()).request().get().getStatus();
+ if (status == 200) {
+ notifyWatchdog();
+ } else {
+ LOGGER.warn("Health check failed with status {}", status);
}
} else {
- LOGGER.warn("Health check failed with status {}", status);
+ notifyWatchdog();
+ }
+ }
+
+ private void notifyWatchdog() {
+ int result = systemD.sd_notify(0, "WATCHDOG=1");
+ if (result < 0) {
+ LOGGER.warn("Health check notify error {}", result);
}
}
diff --git a/src/main/java/org/traccar/schedule/TaskReports.java b/src/main/java/org/traccar/schedule/TaskReports.java
index 004a6078c..e0fa6f8d6 100644
--- a/src/main/java/org/traccar/schedule/TaskReports.java
+++ b/src/main/java/org/traccar/schedule/TaskReports.java
@@ -18,7 +18,7 @@ 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.component.VEvent;
+import net.fortuna.ical4j.model.Period;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.model.BaseModel;
@@ -39,7 +39,7 @@ import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Request;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.util.Collections;
import java.util.Date;
import java.util.List;
@@ -51,7 +51,7 @@ public class TaskReports implements ScheduleTask {
private static final Logger LOGGER = LoggerFactory.getLogger(TaskReports.class);
- private static final long CHECK_PERIOD_MINUTES = 1;
+ private static final long CHECK_PERIOD_MINUTES = 15;
private final Storage storage;
private final Injector injector;
@@ -77,16 +77,14 @@ public class TaskReports implements ScheduleTask {
Calendar calendar = storage.getObject(Calendar.class, new Request(
new Columns.All(), new Condition.Equals("id", report.getCalendarId())));
- var lastEvents = calendar.findEvents(lastCheck);
- var currentEvents = calendar.findEvents(currentCheck);
+ var lastEvents = calendar.findPeriods(lastCheck);
+ var currentEvents = calendar.findPeriods(currentCheck);
if (!lastEvents.isEmpty() && currentEvents.isEmpty()) {
- VEvent event = lastEvents.iterator().next();
- Date from = event.getStartDate().getDate();
- Date to = event.getEndDate().getDate();
+ Period period = lastEvents.iterator().next();
RequestScoper scope = ServletScopes.scopeRequest(Collections.emptyMap());
try (RequestScoper.CloseableScope ignored = scope.open()) {
- executeReport(report, from, to);
+ executeReport(report, period.getStart(), period.getEnd());
}
}
}
diff --git a/src/main/java/org/traccar/schedule/TaskWebSocketKeepalive.java b/src/main/java/org/traccar/schedule/TaskWebSocketKeepalive.java
index e6c2e8b6d..d9e0c6f0b 100644
--- a/src/main/java/org/traccar/schedule/TaskWebSocketKeepalive.java
+++ b/src/main/java/org/traccar/schedule/TaskWebSocketKeepalive.java
@@ -17,7 +17,7 @@ package org.traccar.schedule;
import org.traccar.session.ConnectionManager;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
diff --git a/src/main/java/org/traccar/session/ConnectionManager.java b/src/main/java/org/traccar/session/ConnectionManager.java
index 37a42d827..42dcf5ce9 100644
--- a/src/main/java/org/traccar/session/ConnectionManager.java
+++ b/src/main/java/org/traccar/session/ConnectionManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2024 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@ import org.traccar.database.NotificationManager;
import org.traccar.model.BaseModel;
import org.traccar.model.Device;
import org.traccar.model.Event;
+import org.traccar.model.LogRecord;
import org.traccar.model.Position;
import org.traccar.model.User;
import org.traccar.session.cache.CacheManager;
@@ -39,8 +40,8 @@ import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Request;
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Arrays;
@@ -62,9 +63,11 @@ public class ConnectionManager implements BroadcastInterface {
private static final Logger LOGGER = LoggerFactory.getLogger(ConnectionManager.class);
private final long deviceTimeout;
+ private final boolean showUnknownDevices;
private final Map<Long, DeviceSession> sessionsByDeviceId = new ConcurrentHashMap<>();
- private final Map<Endpoint, Map<String, DeviceSession>> sessionsByEndpoint = new ConcurrentHashMap<>();
+ private final Map<SocketAddress, Map<String, DeviceSession>> sessionsByEndpoint = new ConcurrentHashMap<>();
+ private final Map<SocketAddress, String> unknownByEndpoint = new ConcurrentHashMap<>();
private final Config config;
private final CacheManager cacheManager;
@@ -93,6 +96,7 @@ public class ConnectionManager implements BroadcastInterface {
this.broadcastService = broadcastService;
this.deviceLookupService = deviceLookupService;
deviceTimeout = config.getLong(Keys.STATUS_TIMEOUT);
+ showUnknownDevices = config.getBoolean(Keys.WEB_SHOW_UNKNOWN_DEVICES);
broadcastService.registerListener(this);
}
@@ -102,11 +106,10 @@ public class ConnectionManager implements BroadcastInterface {
public DeviceSession getDeviceSession(
Protocol protocol, Channel channel, SocketAddress remoteAddress,
- String... uniqueIds) throws StorageException {
+ String... uniqueIds) throws Exception {
- Endpoint endpoint = new Endpoint(channel, remoteAddress);
Map<String, DeviceSession> endpointSessions = sessionsByEndpoint.getOrDefault(
- endpoint, new ConcurrentHashMap<>());
+ remoteAddress, new ConcurrentHashMap<>());
uniqueIds = Arrays.stream(uniqueIds).filter(Objects::nonNull).toArray(String[]::new);
if (uniqueIds.length > 0) {
@@ -122,28 +125,31 @@ public class ConnectionManager implements BroadcastInterface {
Device device = deviceLookupService.lookup(uniqueIds);
+ String firstUniqueId = uniqueIds[0];
if (device == null && config.getBoolean(Keys.DATABASE_REGISTER_UNKNOWN)) {
- device = addUnknownDevice(uniqueIds[0]);
+ if (firstUniqueId.matches(config.getString(Keys.DATABASE_REGISTER_UNKNOWN_REGEX))) {
+ device = addUnknownDevice(firstUniqueId);
+ }
}
if (device != null) {
+ unknownByEndpoint.remove(remoteAddress);
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);
+ Map<String, DeviceSession> oldEndpointSessions = sessionsByEndpoint.get(oldSession.getRemoteAddress());
if (oldEndpointSessions != null && oldEndpointSessions.size() > 1) {
oldEndpointSessions.remove(device.getUniqueId());
} else {
- sessionsByEndpoint.remove(oldEndpoint);
+ sessionsByEndpoint.remove(oldSession.getRemoteAddress());
}
}
DeviceSession deviceSession = new DeviceSession(
- device.getId(), device.getUniqueId(), protocol, channel, remoteAddress);
+ device.getId(), device.getUniqueId(), device.getModel(), protocol, channel, remoteAddress);
endpointSessions.put(device.getUniqueId(), deviceSession);
- sessionsByEndpoint.put(endpoint, endpointSessions);
+ sessionsByEndpoint.put(remoteAddress, endpointSessions);
sessionsByDeviceId.put(device.getId(), deviceSession);
if (oldSession == null) {
@@ -152,6 +158,7 @@ public class ConnectionManager implements BroadcastInterface {
return deviceSession;
} else {
+ unknownByEndpoint.put(remoteAddress, firstUniqueId);
LOGGER.warn("Unknown device - " + String.join(" ", uniqueIds)
+ " (" + ((InetSocketAddress) remoteAddress).getHostString() + ")");
return null;
@@ -180,16 +187,19 @@ public class ConnectionManager implements BroadcastInterface {
}
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);
+ SocketAddress remoteAddress = channel.remoteAddress();
+ if (remoteAddress != null) {
+ Map<String, DeviceSession> endpointSessions = sessionsByEndpoint.remove(remoteAddress);
+ 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());
}
- sessionsByDeviceId.remove(deviceSession.getDeviceId());
- cacheManager.removeDevice(deviceSession.getDeviceId());
}
+ unknownByEndpoint.remove(remoteAddress);
}
}
@@ -202,8 +212,7 @@ public class ConnectionManager implements BroadcastInterface {
DeviceSession deviceSession = sessionsByDeviceId.remove(deviceId);
if (deviceSession != null) {
cacheManager.removeDevice(deviceId);
- Endpoint endpoint = new Endpoint(deviceSession.getChannel(), deviceSession.getRemoteAddress());
- sessionsByEndpoint.computeIfPresent(endpoint, (e, sessions) -> {
+ sessionsByEndpoint.computeIfPresent(deviceSession.getRemoteAddress(), (e, sessions) -> {
sessions.remove(deviceSession.getUniqueId());
return sessions.isEmpty() ? null : sessions;
});
@@ -325,11 +334,9 @@ public class ConnectionManager implements BroadcastInterface {
}
@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)) {
+ public synchronized <T1 extends BaseModel, T2 extends BaseModel> void invalidatePermission(
+ boolean local, Class<T1> clazz1, long id1, Class<T2> clazz2, long id2, boolean link) {
+ if (link && 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)));
@@ -337,11 +344,34 @@ public class ConnectionManager implements BroadcastInterface {
}
}
+ public synchronized void updateLog(LogRecord record) {
+ var sessions = sessionsByEndpoint.getOrDefault(record.getAddress(), Map.of());
+ if (sessions.isEmpty()) {
+ String unknownUniqueId = unknownByEndpoint.get(record.getAddress());
+ if (unknownUniqueId != null && showUnknownDevices) {
+ record.setUniqueId(unknownUniqueId);
+ listeners.values().stream()
+ .flatMap(Set::stream)
+ .forEach((listener) -> listener.onUpdateLog(record));
+ }
+ } else {
+ var firstEntry = sessions.entrySet().iterator().next();
+ record.setUniqueId(firstEntry.getKey());
+ record.setDeviceId(firstEntry.getValue().getDeviceId());
+ for (long userId : deviceUsers.getOrDefault(record.getDeviceId(), Set.of())) {
+ for (UpdateListener listener : listeners.getOrDefault(userId, Set.of())) {
+ listener.onUpdateLog(record);
+ }
+ }
+ }
+ }
+
public interface UpdateListener {
void onKeepalive();
void onUpdateDevice(Device device);
void onUpdatePosition(Position position);
void onUpdateEvent(Event event);
+ void onUpdateLog(LogRecord record);
}
public synchronized void addListener(long userId, UpdateListener listener) throws StorageException {
diff --git a/src/main/java/org/traccar/session/DeviceSession.java b/src/main/java/org/traccar/session/DeviceSession.java
index 009f90f5a..f124ca7f9 100644
--- a/src/main/java/org/traccar/session/DeviceSession.java
+++ b/src/main/java/org/traccar/session/DeviceSession.java
@@ -29,14 +29,17 @@ public class DeviceSession {
private final long deviceId;
private final String uniqueId;
+ private final String model;
private final Protocol protocol;
private final Channel channel;
private final SocketAddress remoteAddress;
public DeviceSession(
- long deviceId, String uniqueId, Protocol protocol, Channel channel, SocketAddress remoteAddress) {
+ long deviceId, String uniqueId, String model,
+ Protocol protocol, Channel channel, SocketAddress remoteAddress) {
this.deviceId = deviceId;
this.uniqueId = uniqueId;
+ this.model = model;
this.protocol = protocol;
this.channel = channel;
this.remoteAddress = remoteAddress;
@@ -50,6 +53,10 @@ public class DeviceSession {
return uniqueId;
}
+ public String getModel() {
+ return model;
+ }
+
public Channel getChannel() {
return channel;
}
diff --git a/src/main/java/org/traccar/session/Endpoint.java b/src/main/java/org/traccar/session/Endpoint.java
deleted file mode 100644
index 76aac3444..000000000
--- a/src/main/java/org/traccar/session/Endpoint.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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/CacheGraph.java b/src/main/java/org/traccar/session/cache/CacheGraph.java
new file mode 100644
index 000000000..c99997288
--- /dev/null
+++ b/src/main/java/org/traccar/session/cache/CacheGraph.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.session.cache;
+
+import org.traccar.model.BaseModel;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Stream;
+
+public class CacheGraph {
+
+ private final Map<CacheKey, CacheNode> roots = new HashMap<>();
+ private final WeakValueMap<CacheKey, CacheNode> nodes = new WeakValueMap<>();
+
+ void addObject(BaseModel value) {
+ CacheKey key = new CacheKey(value);
+ CacheNode node = new CacheNode(value);
+ roots.put(key, node);
+ nodes.put(key, node);
+ }
+
+ void removeObject(Class<? extends BaseModel> clazz, long id) {
+ CacheKey key = new CacheKey(clazz, id);
+ CacheNode node = nodes.remove(key);
+ if (node != null) {
+ node.getAllLinks(false).forEach(child -> child.getLinks(key.getClazz(), true).remove(node));
+ }
+ roots.remove(key);
+ }
+
+ @SuppressWarnings("unchecked")
+ <T extends BaseModel> T getObject(Class<T> clazz, long id) {
+ CacheNode node = nodes.get(new CacheKey(clazz, id));
+ return node != null ? (T) node.getValue() : null;
+ }
+
+ <T extends BaseModel> Stream<T> getObjects(
+ Class<? extends BaseModel> fromClass, long fromId,
+ Class<T> clazz, Set<Class<? extends BaseModel>> proxies, boolean forward) {
+
+ CacheNode rootNode = nodes.get(new CacheKey(fromClass, fromId));
+ if (rootNode != null) {
+ return getObjectStream(rootNode, clazz, proxies, forward);
+ } else {
+ return Stream.empty();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private <T extends BaseModel> Stream<T> getObjectStream(
+ CacheNode rootNode, Class<T> clazz, Set<Class<? extends BaseModel>> proxies, boolean forward) {
+
+ if (proxies.contains(clazz)) {
+ return Stream.empty();
+ }
+
+ var directSteam = rootNode.getLinks(clazz, forward).stream()
+ .map(node -> (T) node.getValue());
+
+ var proxyStream = proxies.stream()
+ .flatMap(proxyClass -> rootNode.getLinks(proxyClass, forward).stream()
+ .flatMap(node -> getObjectStream(node, clazz, proxies, forward)));
+
+ return Stream.concat(directSteam, proxyStream);
+ }
+
+ void updateObject(BaseModel value) {
+ CacheNode node = nodes.get(new CacheKey(value));
+ if (node != null) {
+ node.setValue(value);
+ }
+ }
+
+ boolean addLink(
+ Class<? extends BaseModel> fromClazz, long fromId,
+ BaseModel toValue) {
+ boolean stop = true;
+ CacheNode fromNode = nodes.get(new CacheKey(fromClazz, fromId));
+ if (fromNode != null) {
+ CacheKey toKey = new CacheKey(toValue);
+ CacheNode toNode = nodes.get(toKey);
+ if (toNode == null) {
+ stop = false;
+ toNode = new CacheNode(toValue);
+ nodes.put(toKey, toNode);
+ }
+ fromNode.getLinks(toValue.getClass(), true).add(toNode);
+ toNode.getLinks(fromClazz, false).add(fromNode);
+ }
+ return stop;
+ }
+
+ void removeLink(
+ Class<? extends BaseModel> fromClazz, long fromId,
+ Class<? extends BaseModel> toClazz, long toId) {
+ CacheNode fromNode = nodes.get(new CacheKey(fromClazz, fromId));
+ if (fromNode != null) {
+ CacheNode toNode = nodes.get(new CacheKey(toClazz, toId));
+ if (toNode != null) {
+ fromNode.getLinks(toClazz, true).remove(toNode);
+ toNode.getLinks(fromClazz, false).remove(fromNode);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder stringBuilder = new StringBuilder();
+ for (CacheNode node : roots.values()) {
+ printNode(stringBuilder, node, "");
+ }
+ return stringBuilder.toString().trim();
+ }
+
+ private void printNode(StringBuilder stringBuilder, CacheNode node, String indentation) {
+ stringBuilder
+ .append('\n')
+ .append(indentation)
+ .append(node.getValue().getClass().getSimpleName())
+ .append('(').append(node.getValue().getId()).append(')');
+ node.getAllLinks(true).forEach(child -> printNode(stringBuilder, child, indentation + " "));
+ }
+
+}
diff --git a/src/main/java/org/traccar/session/cache/CacheKey.java b/src/main/java/org/traccar/session/cache/CacheKey.java
index 23145e34b..f27d5fbf5 100644
--- a/src/main/java/org/traccar/session/cache/CacheKey.java
+++ b/src/main/java/org/traccar/session/cache/CacheKey.java
@@ -33,6 +33,10 @@ class CacheKey {
this.id = id;
}
+ public Class<? extends BaseModel> getClazz() {
+ return clazz;
+ }
+
public boolean classIs(Class<? extends BaseModel> clazz) {
return clazz.equals(this.clazz);
}
diff --git a/src/main/java/org/traccar/session/cache/CacheManager.java b/src/main/java/org/traccar/session/cache/CacheManager.java
index 9d2350012..064e5672f 100644
--- a/src/main/java/org/traccar/session/cache/CacheManager.java
+++ b/src/main/java/org/traccar/session/cache/CacheManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,8 +15,8 @@
*/
package org.traccar.session.cache;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
import org.traccar.broadcast.BroadcastInterface;
import org.traccar.broadcast.BroadcastService;
import org.traccar.config.Config;
@@ -30,8 +30,10 @@ import org.traccar.model.Group;
import org.traccar.model.GroupedModel;
import org.traccar.model.Maintenance;
import org.traccar.model.Notification;
+import org.traccar.model.ObjectOperation;
+import org.traccar.model.Permission;
import org.traccar.model.Position;
-import org.traccar.model.ScheduledModel;
+import org.traccar.model.Schedulable;
import org.traccar.model.Server;
import org.traccar.model.User;
import org.traccar.storage.Storage;
@@ -40,19 +42,10 @@ import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Request;
-import javax.inject.Inject;
-import javax.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.atomic.AtomicInteger;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;
@@ -60,10 +53,8 @@ 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 static final Set<Class<? extends BaseModel>> GROUPED_CLASSES =
+ Set.of(Attribute.class, Driver.class, Geofence.class, Maintenance.class, Notification.class);
private final Config config;
private final Storage storage;
@@ -71,24 +62,26 @@ public class CacheManager implements BroadcastInterface {
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 final CacheGraph graph = new CacheGraph();
private Server server;
- private final Map<Long, List<User>> notificationUsers = new HashMap<>();
+ private final Map<Long, Position> devicePositions = new HashMap<>();
+ private final Map<Long, AtomicInteger> deviceReferences = new HashMap<>();
@Inject
public CacheManager(Config config, Storage storage, BroadcastService broadcastService) throws StorageException {
this.config = config;
this.storage = storage;
this.broadcastService = broadcastService;
- invalidateServer();
- invalidateUsers();
+ server = storage.getObject(Server.class, new Request(new Columns.All()));
broadcastService.registerListener(this);
}
+ @Override
+ public String toString() {
+ return graph.toString();
+ }
+
public Config getConfig() {
return config;
}
@@ -96,29 +89,17 @@ public class CacheManager implements BroadcastInterface {
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;
+ return graph.getObject(clazz, id);
} finally {
lock.readLock().unlock();
}
}
- public <T extends BaseModel> List<T> getDeviceObjects(long deviceId, Class<T> clazz) {
+ public <T extends BaseModel> Set<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();
- }
+ return graph.getObjects(Device.class, deviceId, clazz, Set.of(Group.class), true)
+ .collect(Collectors.toUnmodifiableSet());
} finally {
lock.readLock().unlock();
}
@@ -142,37 +123,45 @@ public class CacheManager implements BroadcastInterface {
}
}
- public List<User> getNotificationUsers(long notificationId, long deviceId) {
+ public Set<User> getNotificationUsers(long notificationId, long deviceId) {
try {
lock.readLock().lock();
- var users = deviceLinks.get(deviceId).get(User.class).stream()
+ Set<User> deviceUsers = getDeviceObjects(deviceId, User.class);
+ return graph.getObjects(Notification.class, notificationId, User.class, Set.of(), false)
+ .filter(deviceUsers::contains)
.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 Set<Notification> getDeviceNotifications(long deviceId) {
+ try {
+ lock.readLock().lock();
+ var direct = graph.getObjects(Device.class, deviceId, Notification.class, Set.of(Group.class), true)
+ .map(BaseModel::getId)
+ .collect(Collectors.toUnmodifiableSet());
+ return graph.getObjects(Device.class, deviceId, Notification.class, Set.of(Group.class, User.class), true)
+ .filter(notification -> notification.getAlways() || direct.contains(notification.getId()))
+ .collect(Collectors.toUnmodifiableSet());
+ } finally {
+ lock.readLock().unlock();
+ }
}
- public void addDevice(long deviceId) throws StorageException {
+ public void addDevice(long deviceId) throws Exception {
try {
lock.writeLock().lock();
- Integer references = deviceReferences.get(deviceId);
- if (references != null) {
- references += 1;
- } else {
- unsafeAddDevice(deviceId);
- references = 1;
+ if (deviceReferences.computeIfAbsent(deviceId, k -> new AtomicInteger()).getAndIncrement() <= 0) {
+ Device device = storage.getObject(Device.class, new Request(
+ new Columns.All(), new Condition.Equals("id", deviceId)));
+ graph.addObject(device);
+ initializeCache(device);
+ if (device.getPositionId() > 0) {
+ devicePositions.put(deviceId, storage.getObject(Position.class, new Request(
+ new Columns.All(), new Condition.Equals("id", device.getPositionId()))));
+ }
}
- deviceReferences.put(deviceId, references);
} finally {
lock.writeLock().unlock();
}
@@ -181,15 +170,10 @@ public class CacheManager implements BroadcastInterface {
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);
- }
+ if (deviceReferences.computeIfAbsent(deviceId, k -> new AtomicInteger()).incrementAndGet() <= 0) {
+ graph.removeObject(Device.class, deviceId);
+ devicePositions.remove(deviceId);
+ deviceReferences.remove(deviceId);
}
} finally {
lock.writeLock().unlock();
@@ -199,7 +183,7 @@ public class CacheManager implements BroadcastInterface {
public void updatePosition(Position position) {
try {
lock.writeLock().lock();
- if (deviceLinks.containsKey(position.getDeviceId())) {
+ if (deviceReferences.containsKey(position.getDeviceId())) {
devicePositions.put(position.getDeviceId(), position);
}
} finally {
@@ -208,218 +192,140 @@ public class CacheManager implements BroadcastInterface {
}
@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 {
+ public <T extends BaseModel> void invalidateObject(
+ boolean local, Class<T> clazz, long id, ObjectOperation operation) throws Exception {
if (local) {
- broadcastService.invalidateObject(true, object.getClass(), object.getId());
+ broadcastService.invalidateObject(true, clazz, id, operation);
}
- if (object instanceof Server) {
- invalidateServer();
+ if (operation == ObjectOperation.DELETE) {
+ graph.removeObject(clazz, id);
+ }
+ if (operation != ObjectOperation.UPDATE) {
return;
}
- if (object instanceof User) {
- invalidateUsers();
+
+ if (clazz.equals(Server.class)) {
+ server = storage.getObject(Server.class, new Request(new Columns.All()));
return;
}
- boolean invalidate = false;
- var before = getObject(object.getClass(), object.getId());
+ var after = storage.getObject(clazz, new Request(new Columns.All(), new Condition.Equals("id", id)));
+ if (after == null) {
+ return;
+ }
+ var before = getObject(after.getClass(), after.getId());
if (before == null) {
return;
- } else if (object instanceof GroupedModel) {
- if (((GroupedModel) before).getGroupId() != ((GroupedModel) object).getGroupId()) {
- invalidate = true;
- }
- } else if (object instanceof ScheduledModel) {
- if (((ScheduledModel) before).getCalendarId() != ((ScheduledModel) 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();
+
+ if (after instanceof GroupedModel) {
+ long beforeGroupId = ((GroupedModel) before).getGroupId();
+ long afterGroupId = ((GroupedModel) after).getGroupId();
+ if (beforeGroupId != afterGroupId) {
+ if (beforeGroupId > 0) {
+ invalidatePermission(clazz, id, Group.class, beforeGroupId, false);
+ }
+ if (afterGroupId > 0) {
+ invalidatePermission(clazz, id, Group.class, afterGroupId, true);
+ }
}
+ } else if (after instanceof Schedulable) {
+ long beforeCalendarId = ((Schedulable) before).getCalendarId();
+ long afterCalendarId = ((Schedulable) after).getCalendarId();
+ if (beforeCalendarId != afterCalendarId) {
+ if (beforeCalendarId > 0) {
+ invalidatePermission(clazz, id, Calendar.class, beforeCalendarId, false);
+ }
+ if (afterCalendarId > 0) {
+ invalidatePermission(clazz, id, Calendar.class, afterCalendarId, true);
+ }
+ }
+ // TODO handle notification always change
}
- }
- public <T extends BaseModel> void invalidate(Class<T> clazz, long id) throws StorageException {
- invalidate(new CacheKey(clazz, id));
+ graph.updateObject(after);
}
@Override
- public void invalidatePermission(
- boolean local,
- Class<? extends BaseModel> clazz1, long id1,
- Class<? extends BaseModel> clazz2, long id2) {
+ public <T1 extends BaseModel, T2 extends BaseModel> void invalidatePermission(
+ boolean local, Class<T1> clazz1, long id1, Class<T2> clazz2, long id2, boolean link) throws Exception {
if (local) {
- broadcastService.invalidatePermission(true, clazz1, id1, clazz2, id2);
+ broadcastService.invalidatePermission(true, clazz1, id1, clazz2, id2, link);
}
- try {
- invalidate(new CacheKey(clazz1, id1), new CacheKey(clazz2, id2));
- } catch (StorageException e) {
- throw new RuntimeException(e);
+ if (clazz1.equals(User.class) && GroupedModel.class.isAssignableFrom(clazz2)) {
+ invalidatePermission(clazz2, id2, clazz1, id1, link);
+ } else {
+ invalidatePermission(clazz1, id1, clazz2, id2, link);
}
}
- private void invalidateServer() throws StorageException {
- server = storage.getObject(Server.class, new Request(new Columns.All()));
- }
+ private <T1 extends BaseModel, T2 extends BaseModel> void invalidatePermission(
+ Class<T1> fromClass, long fromId, Class<T2> toClass, long toId, boolean link) throws Exception {
- 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);
- });
- }
+ boolean groupLink = GroupedModel.class.isAssignableFrom(fromClass) && toClass.equals(Group.class);
+ boolean calendarLink = Schedulable.class.isAssignableFrom(fromClass) && toClass.equals(Calendar.class);
+ boolean userLink = fromClass.equals(User.class) && toClass.equals(Notification.class);
- private void addObject(long deviceId, BaseModel object) {
- deviceCache.computeIfAbsent(new CacheKey(object), k -> new CacheValue(object)).retain(deviceId);
- }
+ boolean groupedLinks = GroupedModel.class.isAssignableFrom(fromClass)
+ && (GROUPED_CLASSES.contains(toClass) || toClass.equals(User.class));
+
+ if (!groupLink && !calendarLink && !userLink && !groupedLinks) {
+ return;
+ }
- 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);
-
- 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;
+ if (link) {
+ BaseModel object = storage.getObject(toClass, new Request(
+ new Columns.All(), new Condition.Equals("id", toId)));
+ if (!graph.addLink(fromClass, fromId, object)) {
+ initializeCache(object);
}
+ } else {
+ graph.removeLink(fromClass, fromId, toClass, toId);
+ }
+ }
- 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 ScheduledModel) {
- var scheduled = (ScheduledModel) object;
- if (scheduled.getCalendarId() > 0) {
- var calendar = storage.getObject(Calendar.class, new Request(
- new Columns.All(), new Condition.Equals("id", scheduled.getCalendarId())));
- links.computeIfAbsent(Notification.class, k -> new LinkedHashSet<>())
- .add(calendar.getId());
- addObject(deviceId, calendar);
- }
- }
+ private void initializeCache(BaseModel object) throws Exception {
+ if (object instanceof User) {
+ for (Permission permission : storage.getPermissions(User.class, Notification.class)) {
+ if (permission.getOwnerId() == object.getId()) {
+ invalidatePermission(
+ permission.getOwnerClass(), permission.getOwnerId(),
+ permission.getPropertyClass(), permission.getPropertyId(), true);
}
}
+ } else {
+ if (object instanceof GroupedModel) {
+ long groupId = ((GroupedModel) object).getGroupId();
+ if (groupId > 0) {
+ invalidatePermission(object.getClass(), object.getId(), Group.class, groupId, true);
+ }
- 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(Notification.class, k -> new LinkedHashSet<>())
- .add(calendar.getId());
- addObject(deviceId, calendar);
+ for (Permission permission : storage.getPermissions(User.class, object.getClass())) {
+ if (permission.getPropertyId() == object.getId()) {
+ invalidatePermission(
+ object.getClass(), object.getId(), User.class, permission.getOwnerId(), true);
}
}
- }
- 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()))));
+ for (Class<? extends BaseModel> clazz : GROUPED_CLASSES) {
+ for (Permission permission : storage.getPermissions(object.getClass(), clazz)) {
+ if (permission.getOwnerId() == object.getId()) {
+ invalidatePermission(
+ object.getClass(), object.getId(), clazz, permission.getPropertyId(), true);
+ }
+ }
+ }
}
- }
- }
-
- 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;
+ if (object instanceof Schedulable) {
+ long calendarId = ((Schedulable) object).getCalendarId();
+ if (calendarId > 0) {
+ invalidatePermission(object.getClass(), object.getId(), Calendar.class, calendarId, 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/CacheNode.java b/src/main/java/org/traccar/session/cache/CacheNode.java
new file mode 100644
index 000000000..7b584f81a
--- /dev/null
+++ b/src/main/java/org/traccar/session/cache/CacheNode.java
@@ -0,0 +1,40 @@
+package org.traccar.session.cache;
+
+import org.traccar.model.BaseModel;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Stream;
+
+public class CacheNode {
+
+ private BaseModel value;
+
+ private final Map<Class<? extends BaseModel>, Set<CacheNode>> links = new HashMap<>();
+ private final Map<Class<? extends BaseModel>, Set<CacheNode>> backlinks = new HashMap<>();
+
+ public CacheNode(BaseModel value) {
+ this.value = value;
+ }
+
+ public BaseModel getValue() {
+ return value;
+ }
+
+ public void setValue(BaseModel value) {
+ this.value = value;
+ }
+
+ public Set<CacheNode> getLinks(Class<? extends BaseModel> clazz, boolean forward) {
+ var map = forward ? links : backlinks;
+ return map.computeIfAbsent(clazz, k -> new HashSet<>());
+ }
+
+ public Stream<CacheNode> getAllLinks(boolean forward) {
+ var map = forward ? links : backlinks;
+ return map.values().stream().flatMap(Set::stream);
+ }
+
+}
diff --git a/src/main/java/org/traccar/session/cache/CacheValue.java b/src/main/java/org/traccar/session/cache/CacheValue.java
deleted file mode 100644
index 1f0383ce5..000000000
--- a/src/main/java/org/traccar/session/cache/CacheValue.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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/cache/WeakValueMap.java b/src/main/java/org/traccar/session/cache/WeakValueMap.java
new file mode 100644
index 000000000..8323e2c30
--- /dev/null
+++ b/src/main/java/org/traccar/session/cache/WeakValueMap.java
@@ -0,0 +1,44 @@
+/*
+ * 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.session.cache;
+
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.Map;
+
+public class WeakValueMap<K, V> {
+
+ private final Map<K, WeakReference<V>> map = new HashMap<>();
+
+ public void put(K key, V value) {
+ map.put(key, new WeakReference<>(value));
+ }
+
+ public V get(K key) {
+ WeakReference<V> weakReference = map.get(key);
+ return (weakReference != null) ? weakReference.get() : null;
+ }
+
+ public V remove(K key) {
+ WeakReference<V> weakReference = map.remove(key);
+ return (weakReference != null) ? weakReference.get() : null;
+ }
+
+ private void clean() {
+ map.entrySet().removeIf(entry -> entry.getValue().get() == null);
+ }
+
+}
diff --git a/src/main/java/org/traccar/session/state/OverspeedProcessor.java b/src/main/java/org/traccar/session/state/OverspeedProcessor.java
index 62f6a3de2..221b51ff5 100644
--- a/src/main/java/org/traccar/session/state/OverspeedProcessor.java
+++ b/src/main/java/org/traccar/session/state/OverspeedProcessor.java
@@ -26,40 +26,46 @@ public final class OverspeedProcessor {
}
public static void updateState(
- OverspeedState state, Position position, double speedLimit, long minimalDuration, long geofenceId) {
+ 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;
+ boolean newState = position.getSpeed() > speedLimit * multiplier;
if (newState) {
- 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);
-
- }
- }
+ checkEvent(state, position, speedLimit, minimalDuration);
} else {
state.setOverspeedState(false);
state.setOverspeedTime(null);
state.setOverspeedGeofenceId(0);
}
- } else if (position != null && position.getSpeed() > speedLimit) {
+ } 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/sms/HttpSmsClient.java b/src/main/java/org/traccar/sms/HttpSmsClient.java
index 51b161594..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 - 2022 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");
@@ -21,11 +21,11 @@ import org.traccar.config.Keys;
import org.traccar.helper.DataConverter;
import org.traccar.notification.MessageException;
-import javax.ws.rs.client.Client;
-import javax.ws.rs.client.Entity;
-import javax.ws.rs.client.Invocation;
-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;
@@ -57,7 +57,10 @@ public class HttpSmsClient implements SmsManager {
}
}
template = config.getString(Keys.SMS_HTTP_TEMPLATE).trim();
- if (template.charAt(0) == '{' || template.charAt(0) == '[') {
+ 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 {
@@ -67,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) {
diff --git a/src/main/java/org/traccar/sms/SmsManager.java b/src/main/java/org/traccar/sms/SmsManager.java
index 9ab25d9cb..8cf99c9e8 100644
--- a/src/main/java/org/traccar/sms/SmsManager.java
+++ b/src/main/java/org/traccar/sms/SmsManager.java
@@ -20,7 +20,6 @@ import org.traccar.notification.MessageException;
public interface SmsManager {
- void sendMessage(
- String destAddress, String message, boolean command) throws InterruptedException, MessageException;
+ void sendMessage(String destAddress, String message, boolean command) throws MessageException;
}
diff --git a/src/main/java/org/traccar/speedlimit/OverpassSpeedLimitProvider.java b/src/main/java/org/traccar/speedlimit/OverpassSpeedLimitProvider.java
index edf089f37..a25eedb2c 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 - 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,25 @@
*/
package org.traccar.speedlimit;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
import org.traccar.helper.UnitsConverter;
-import javax.json.JsonArray;
-import javax.json.JsonObject;
-import javax.ws.rs.client.AsyncInvoker;
-import javax.ws.rs.client.Client;
-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(Client client, String url) {
+ public OverpassSpeedLimitProvider(Config config, Client client, String url) {
+ int accuracy = config.getInteger(Keys.SPEED_LIMIT_ACCURACY);
this.client = client;
- this.url = url + "?data=[out:json];way[maxspeed](around:100.0,%f,%f);out%%20tags;";
+ this.url = url + "?data=[out:json];way[maxspeed](around:" + accuracy + ",%f,%f);out%%20tags;";
}
private Double parseSpeed(String value) {
diff --git a/src/main/java/org/traccar/storage/DatabaseModule.java b/src/main/java/org/traccar/storage/DatabaseModule.java
index 3e3483818..9d9e5bd5e 100644
--- a/src/main/java/org/traccar/storage/DatabaseModule.java
+++ b/src/main/java/org/traccar/storage/DatabaseModule.java
@@ -29,7 +29,7 @@ import liquibase.resource.ResourceAccessor;
import org.traccar.config.Config;
import org.traccar.config.Keys;
-import javax.inject.Singleton;
+import jakarta.inject.Singleton;
import javax.sql.DataSource;
import java.io.File;
import java.io.IOException;
diff --git a/src/main/java/org/traccar/storage/DatabaseStorage.java b/src/main/java/org/traccar/storage/DatabaseStorage.java
index a049a641c..d20429319 100644
--- a/src/main/java/org/traccar/storage/DatabaseStorage.java
+++ b/src/main/java/org/traccar/storage/DatabaseStorage.java
@@ -27,7 +27,7 @@ import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Order;
import org.traccar.storage.query.Request;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
diff --git a/src/main/java/org/traccar/storage/MemoryStorage.java b/src/main/java/org/traccar/storage/MemoryStorage.java
index f19897ff8..9b5db1209 100644
--- a/src/main/java/org/traccar/storage/MemoryStorage.java
+++ b/src/main/java/org/traccar/storage/MemoryStorage.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -18,35 +18,155 @@ 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) {
diff --git a/src/main/java/org/traccar/web/ConsoleServlet.java b/src/main/java/org/traccar/web/ConsoleServlet.java
index 902a4f7a9..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 - 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,7 +16,7 @@
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.config.Config;
@@ -26,7 +26,7 @@ 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);
@@ -41,7 +41,7 @@ public class ConsoleServlet extends WebServlet {
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);
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..9780c9ede
--- /dev/null
+++ b/src/main/java/org/traccar/web/OverrideFilter.java
@@ -0,0 +1,88 @@
+/*
+ * 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.webmanifest")) {
+
+ 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);
+
+ byte[] data = alteredContent.getBytes();
+ response.setContentLength(data.length);
+ response.getOutputStream().write(data);
+
+ } 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
index 054af652f..1bad33db6 100644
--- a/src/main/java/org/traccar/web/ThrottlingFilter.java
+++ b/src/main/java/org/traccar/web/ThrottlingFilter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -19,13 +19,13 @@ import org.eclipse.jetty.servlets.DoSFilter;
import org.traccar.config.Config;
import org.traccar.config.Keys;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSession;
+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 {
@@ -39,6 +39,7 @@ public class ThrottlingFilter extends DoSFilter {
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
diff --git a/src/main/java/org/traccar/web/WebInjectionManagerFactory.java b/src/main/java/org/traccar/web/WebInjectionManagerFactory.java
index 14d9d3dbc..3e73c41ad 100644
--- a/src/main/java/org/traccar/web/WebInjectionManagerFactory.java
+++ b/src/main/java/org/traccar/web/WebInjectionManagerFactory.java
@@ -23,7 +23,7 @@ import org.jvnet.hk2.guice.bridge.api.GuiceBridge;
import org.jvnet.hk2.guice.bridge.api.GuiceIntoHK2Bridge;
import org.traccar.Main;
-import javax.annotation.Priority;
+import jakarta.annotation.Priority;
@Priority(20)
public class WebInjectionManagerFactory implements InjectionManagerFactory {
diff --git a/src/main/java/org/traccar/web/WebModule.java b/src/main/java/org/traccar/web/WebModule.java
index 0722c5d1e..a32a6f447 100644
--- a/src/main/java/org/traccar/web/WebModule.java
+++ b/src/main/java/org/traccar/web/WebModule.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -23,6 +23,7 @@ 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/WebRequestLog.java b/src/main/java/org/traccar/web/WebRequestLog.java
new file mode 100644
index 000000000..3f3286003
--- /dev/null
+++ b/src/main/java/org/traccar/web/WebRequestLog.java
@@ -0,0 +1,57 @@
+/*
+ * 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.server.Request;
+import org.eclipse.jetty.server.RequestLog;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.DateCache;
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.traccar.api.resource.SessionResource;
+
+import java.util.Locale;
+import java.util.TimeZone;
+
+public class WebRequestLog extends ContainerLifeCycle implements RequestLog {
+
+ private final Writer writer;
+
+ private final DateCache dateCache = new DateCache(
+ "dd/MMM/yyyy:HH:mm:ss ZZZ", Locale.getDefault(), TimeZone.getTimeZone("GMT"));
+
+ public WebRequestLog(Writer writer) {
+ this.writer = writer;
+ addBean(writer);
+ }
+
+ @Override
+ public void log(Request request, Response response) {
+ try {
+ Long userId = (Long) request.getSession().getAttribute(SessionResource.USER_ID_KEY);
+ writer.write(String.format("%s - %s [%s] \"%s %s %s\" %d %d",
+ request.getRemoteHost(),
+ userId != null ? String.valueOf(userId) : "-",
+ dateCache.format(request.getTimeStamp()),
+ request.getMethod(),
+ request.getOriginalURI(),
+ request.getProtocol(),
+ response.getCommittedMetaData().getStatus(),
+ response.getHttpChannel().getBytesWritten()));
+ } catch (Throwable ignored) {
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/web/WebServer.java b/src/main/java/org/traccar/web/WebServer.java
index 79d19cc9b..4759942b1 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.
@@ -21,7 +21,6 @@ import org.eclipse.jetty.http.HttpCookie;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.proxy.AsyncProxyServlet;
-import org.eclipse.jetty.server.CustomRequestLog;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.RequestLogWriter;
import org.eclipse.jetty.server.Server;
@@ -52,19 +51,16 @@ import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.helper.ObjectMapperContextResolver;
-import javax.servlet.DispatcherType;
-import javax.servlet.ServletException;
-import javax.servlet.SessionCookieConfig;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+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;
import java.net.InetSocketAddress;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.EnumSet;
public class WebServer implements LifecycleObject {
@@ -103,14 +99,8 @@ public class WebServer implements LifecycleObject {
@Override
protected void handleErrorPage(
HttpServletRequest request, Writer writer, int code, String message) throws IOException {
- Path index = Paths.get(config.getString(Keys.WEB_PATH), "index.html");
- if (code == HttpStatus.NOT_FOUND_404
- && !request.getPathInfo().startsWith("/api/") && Files.exists(index)) {
- writer.write(Files.readString(index));
- } else {
- writer.write("<!DOCTYPE><html><head><title>Error</title></head><html><body>"
- + code + " - " + HttpStatus.getMessage(code) + "</body></html>");
- }
+ writer.write("<!DOCTYPE><html><head><title>Error</title></head><html><body>"
+ + code + " - " + HttpStatus.getMessage(code) + "</body></html>");
}
});
@@ -124,8 +114,7 @@ public class WebServer implements LifecycleObject {
RequestLogWriter logWriter = new RequestLogWriter(config.getString(Keys.WEB_REQUEST_LOG_PATH));
logWriter.setAppend(true);
logWriter.setRetainDays(config.getInteger(Keys.WEB_REQUEST_LOG_RETAIN_DAYS));
- CustomRequestLog requestLog = new CustomRequestLog(logWriter, CustomRequestLog.NCSA_FORMAT);
- server.setRequestLog(requestLog);
+ server.setRequestLog(new WebRequestLog(logWriter));
}
}
@@ -150,7 +139,7 @@ public class WebServer implements LifecycleObject {
}
private void initWebApp(ServletContextHandler servletHandler) {
- ServletHolder servletHolder = new ServletHolder(DefaultServlet.class);
+ 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)) {
@@ -202,14 +191,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);
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 c784150dd..ce19d8ace 100644
--- a/src/test/java/org/traccar/BaseTest.java
+++ b/src/test/java/org/traccar/BaseTest.java
@@ -32,8 +32,9 @@ public class BaseTest {
decoder.setCacheManager(cacheManager);
var connectionManager = mock(ConnectionManager.class);
var uniqueIdsProvided = new HashSet<Boolean>();
- when(connectionManager.getDeviceSession(any(), any(), any(), any())).thenAnswer(invocation -> {
- var mock = new DeviceSession(1L, "", mock(Protocol.class), mock(Channel.class), mock(SocketAddress.class));
+ when(connectionManager.getDeviceSession(any(), any(), any(), any(String[].class))).thenAnswer(invocation -> {
+ var mock = new DeviceSession(
+ 1L, "", null, mock(Protocol.class), mock(Channel.class), mock(SocketAddress.class));
if (uniqueIdsProvided.isEmpty()) {
if (invocation.getArguments().length > 3) {
uniqueIdsProvided.add(true);
diff --git a/src/test/java/org/traccar/ProtocolTest.java b/src/test/java/org/traccar/ProtocolTest.java
index 5e37f44b9..23ba562f8 100644
--- a/src/test/java/org/traccar/ProtocolTest.java
+++ b/src/test/java/org/traccar/ProtocolTest.java
@@ -26,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 {
@@ -112,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;
}
@@ -155,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);
}
@@ -167,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;
@@ -179,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)) {
@@ -262,6 +261,10 @@ public class ProtocolTest extends BaseTest {
assertTrue(attributes.get(Position.KEY_FUEL_LEVEL) instanceof Number);
}
+ if (attributes.containsKey(Position.KEY_FUEL_USED)) {
+ assertTrue(attributes.get(Position.KEY_FUEL_USED) instanceof Number);
+ }
+
if (attributes.containsKey(Position.KEY_POWER)) {
assertTrue(attributes.get(Position.KEY_POWER) instanceof Number);
}
@@ -331,11 +334,11 @@ public class ProtocolTest extends BaseTest {
}
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(
@@ -344,8 +347,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/calendar/CalendarTest.java b/src/test/java/org/traccar/calendar/CalendarTest.java
index def67ff76..4106f89a9 100644
--- a/src/test/java/org/traccar/calendar/CalendarTest.java
+++ b/src/test/java/org/traccar/calendar/CalendarTest.java
@@ -1,7 +1,7 @@
package org.traccar.calendar;
import net.fortuna.ical4j.data.ParserException;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import org.traccar.model.Calendar;
import java.io.IOException;
@@ -11,7 +11,8 @@ import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
-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 {
@@ -45,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 8ba6dace6..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 org.junit.jupiter.api.Test;
-import static org.junit.Assert.assertEquals;
+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
index 522958052..f0e354620 100644
--- a/src/test/java/org/traccar/forward/PositionForwarderUrlTest.java
+++ b/src/test/java/org/traccar/forward/PositionForwarderUrlTest.java
@@ -1,13 +1,13 @@
package org.traccar.forward;
-import org.junit.Test;
+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.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
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 ff33b1f1c..ef2dd062d 100644
--- a/src/test/java/org/traccar/geocoder/GeocoderTest.java
+++ b/src/test/java/org/traccar/geocoder/GeocoderTest.java
@@ -1,13 +1,13 @@
package org.traccar.geocoder;
-import org.junit.Ignore;
-import org.junit.Test;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
-import javax.ws.rs.client.Client;
-import javax.ws.rs.client.ClientBuilder;
+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 {
@@ -17,7 +17,7 @@ public class GeocoderTest {
private final Client client = ClientBuilder.newClient();
- @Ignore
+ @Disabled
@Test
public void testGoogle() {
Geocoder geocoder = new GoogleGeocoder(client, null, null, 0, new AddressFormat());
@@ -25,7 +25,7 @@ public class GeocoderTest {
assertEquals("1 Ibn Shaprut St, Jerusalem, Jerusalem District, IL", address);
}
- @Ignore
+ @Disabled
@Test
public void testNominatim() {
Geocoder geocoder = new NominatimGeocoder(client, null, null, null, 0, new AddressFormat());
@@ -33,7 +33,7 @@ public class GeocoderTest {
assertEquals("35 West 9th Street, NYC, New York, US", address);
}
- @Ignore
+ @Disabled
@Test
public void testGisgraphy() {
Geocoder geocoder = new GisgraphyGeocoder(client, null, 0, new AddressFormat());
@@ -41,7 +41,7 @@ public class GeocoderTest {
assertEquals("Rue du Jardinet, Paris, ÃŽle-de-France, FR", address);
}
- @Ignore
+ @Disabled
@Test
public void testOpenCage() {
Geocoder geocoder = new OpenCageGeocoder(
@@ -50,7 +50,7 @@ public class GeocoderTest {
assertEquals("Charleston Road, California, US", address);
}
- @Ignore
+ @Disabled
@Test
public void testGeocodeFarm() {
Geocoder geocoder = new GeocodeFarmGeocoder(client, null, null, 0, new AddressFormat());
@@ -58,7 +58,7 @@ public class GeocoderTest {
assertEquals("Estrella Avenue, Arcadia, California, United States", address);
}
- @Ignore
+ @Disabled
@Test
public void testGeocodeXyz() {
Geocoder geocoder = new GeocodeXyzGeocoder(client, null, 0, new AddressFormat());
@@ -66,7 +66,7 @@ public class GeocoderTest {
assertEquals("605 ESTRELLA AVE, ARCADIA, California United States of America, US", address);
}
- @Ignore
+ @Disabled
@Test
public void testBan() {
Geocoder geocoder = new BanGeocoder(client, 0, new AddressFormat("%f [%d], %c"));
@@ -74,15 +74,15 @@ public class GeocoderTest {
assertEquals("8 Avenue Gustave Eiffel 75007 Paris [75, Paris, ÃŽle-de-France], FR", address);
}
- @Ignore
+ @Disabled
@Test
public void testHere() {
- Geocoder geocoder = new HereGeocoder(client, null, "", "", null, 0, new AddressFormat());
+ Geocoder geocoder = new HereGeocoder(client, null, "aDc9qgsCpRbO9ioJIIAXzF6JYU7w8H5O260e9hsGrms", null, 0, new AddressFormat());
String address = geocoder.getAddress(48.8575, 2.2944, null);
- assertEquals("6 Avenue Gustave Eiffel, Paris, ÃŽle-de-France, FRA", address);
+ assertEquals("1 Tour Eiffel, Paris, ÃŽle-de-France, FRA", address);
}
- @Ignore
+ @Disabled
@Test
public void testMapmyIndia() {
Geocoder geocoder = new MapmyIndiaGeocoder(client, "", "", 0, new AddressFormat("%f"));
@@ -90,7 +90,7 @@ public class GeocoderTest {
assertEquals("New Delhi, Delhi. 1 m from India Gate pin-110001 (India)", address);
}
- @Ignore
+ @Disabled
@Test
public void testPositionStack() {
Geocoder geocoder = new PositionStackGeocoder(client, "", 0, new AddressFormat("%f"));
@@ -98,7 +98,7 @@ public class GeocoderTest {
assertEquals("India Gate, New Delhi, India", address);
}
- @Ignore
+ @Disabled
@Test
public void testMapbox() {
Geocoder geocoder = new MapboxGeocoder(client, "", 0, new AddressFormat("%f"));
@@ -106,7 +106,7 @@ public class GeocoderTest {
assertEquals("120 East 13th Street, New York, New York 10003, United States", address);
}
- @Ignore
+ @Disabled
@Test
public void testMapTiler() {
Geocoder geocoder = new MapTilerGeocoder(client, "", 0, new AddressFormat());
@@ -114,7 +114,7 @@ public class GeocoderTest {
assertEquals("East 13th Street, New York City, New York, United States", address);
}
- @Ignore
+ @Disabled
@Test
public void testGeoapify() {
Geocoder geocoder = new GeoapifyGeocoder(client, "", null, 0, new AddressFormat());
diff --git a/src/test/java/org/traccar/geofence/GeofenceCircleTest.java b/src/test/java/org/traccar/geofence/GeofenceCircleTest.java
index 9a02cec76..106b041fc 100644
--- a/src/test/java/org/traccar/geofence/GeofenceCircleTest.java
+++ b/src/test/java/org/traccar/geofence/GeofenceCircleTest.java
@@ -1,12 +1,12 @@
package org.traccar.geofence;
-import org.junit.Test;
+import org.junit.jupiter.api.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 GeofenceCircleTest {
diff --git a/src/test/java/org/traccar/geofence/GeofencePolygonTest.java b/src/test/java/org/traccar/geofence/GeofencePolygonTest.java
index 5baecd771..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 org.junit.Test;
+import org.junit.jupiter.api.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 {
diff --git a/src/test/java/org/traccar/geofence/GeofencePolylineTest.java b/src/test/java/org/traccar/geofence/GeofencePolylineTest.java
index b7ee14510..9b7bbb7d1 100644
--- a/src/test/java/org/traccar/geofence/GeofencePolylineTest.java
+++ b/src/test/java/org/traccar/geofence/GeofencePolylineTest.java
@@ -1,15 +1,15 @@
package org.traccar.geofence;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.model.Geofence;
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;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
diff --git a/src/test/java/org/traccar/geolocation/GeolocationProviderTest.java b/src/test/java/org/traccar/geolocation/GeolocationProviderTest.java
index 876b6b688..da5ae3340 100644
--- a/src/test/java/org/traccar/geolocation/GeolocationProviderTest.java
+++ b/src/test/java/org/traccar/geolocation/GeolocationProviderTest.java
@@ -1,22 +1,22 @@
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 javax.ws.rs.client.Client;
-import javax.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
public class GeolocationProviderTest extends BaseTest {
private final Client client = ClientBuilder.newClient();
- @Ignore
+ @Disabled
@Test
public void testMozilla() throws Exception {
MozillaGeolocationProvider provider = new MozillaGeolocationProvider(client, null);
diff --git a/src/test/java/org/traccar/handler/ComputedAttributesTest.java b/src/test/java/org/traccar/handler/ComputedAttributesTest.java
index 2668c4f14..e2af703c2 100644
--- a/src/test/java/org/traccar/handler/ComputedAttributesTest.java
+++ b/src/test/java/org/traccar/handler/ComputedAttributesTest.java
@@ -1,13 +1,13 @@
package org.traccar.handler;
-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 java.util.Date;
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
public class ComputedAttributesTest {
@@ -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 a18b14edd..7d2f1e2e3 100644
--- a/src/test/java/org/traccar/handler/DistanceHandlerTest.java
+++ b/src/test/java/org/traccar/handler/DistanceHandlerTest.java
@@ -1,11 +1,11 @@
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 {
diff --git a/src/test/java/org/traccar/handler/FilterHandlerTest.java b/src/test/java/org/traccar/handler/FilterHandlerTest.java
index a1102da88..36bb84f19 100644
--- a/src/test/java/org/traccar/handler/FilterHandlerTest.java
+++ b/src/test/java/org/traccar/handler/FilterHandlerTest.java
@@ -1,7 +1,7 @@
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;
@@ -11,8 +11,8 @@ 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;
@@ -23,16 +23,17 @@ public class FilterHandlerTest extends BaseTest {
private FilterHandler passingHandler;
private FilterHandler filteringHandler;
- @Before
+ @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);
- passingHandler = new FilterHandler(config, cacheManager, null);
+ when(cacheManager.getObject(any(), anyLong())).thenReturn(mock(Device.class));
+ passingHandler = new FilterHandler(config, cacheManager, null, null);
}
- @Before
+ @BeforeEach
public void filteringHandler() {
var config = mock(Config.class);
when(config.getBoolean(Keys.FILTER_ENABLE)).thenReturn(true);
@@ -50,7 +51,7 @@ public class FilterHandlerTest extends BaseTest {
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);
+ filteringHandler = new FilterHandler(config, cacheManager, null, null);
}
private Position createPosition(Date time, boolean valid, double speed) {
@@ -71,18 +72,18 @@ public class FilterHandlerTest extends BaseTest {
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(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(new Date(), false, 10);
- assertNull(filteringHandler.handlePosition(position));
- assertNotNull(passingHandler.handlePosition(position));
+ assertTrue(filteringHandler.filter(position));
+ assertFalse(passingHandler.filter(position));
}
@@ -92,7 +93,7 @@ public class FilterHandlerTest extends BaseTest {
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 93fd16206..10cdf6a90 100644
--- a/src/test/java/org/traccar/handler/MotionHandlerTest.java
+++ b/src/test/java/org/traccar/handler/MotionHandlerTest.java
@@ -1,10 +1,15 @@
package org.traccar.handler;
-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.reports.common.TripsConfig;
+import org.traccar.session.cache.CacheManager;
-import static org.junit.Assert.assertEquals;
+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;
@@ -13,10 +18,13 @@ public class MotionHandlerTest {
@Test
public void testCalculateMotion() {
- TripsConfig tripsConfig = mock(TripsConfig.class);
- when(tripsConfig.getSpeedThreshold()).thenReturn(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(tripsConfig);
+ 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 550a93da3..66dc55c85 100644
--- a/src/test/java/org/traccar/handler/events/AlertEventHandlerTest.java
+++ b/src/test/java/org/traccar/handler/events/AlertEventHandlerTest.java
@@ -1,6 +1,6 @@
package org.traccar.handler.events;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import org.traccar.BaseTest;
import org.traccar.config.Config;
import org.traccar.model.Event;
@@ -9,8 +9,8 @@ import org.traccar.session.cache.CacheManager;
import java.util.Map;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
+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 {
diff --git a/src/test/java/org/traccar/handler/events/CommandResultEventHandlerTest.java b/src/test/java/org/traccar/handler/events/CommandResultEventHandlerTest.java
index 4997a0e0f..bc24e42f5 100644
--- a/src/test/java/org/traccar/handler/events/CommandResultEventHandlerTest.java
+++ b/src/test/java/org/traccar/handler/events/CommandResultEventHandlerTest.java
@@ -1,14 +1,14 @@
package org.traccar.handler.events;
-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.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
public class CommandResultEventHandlerTest extends BaseTest {
diff --git a/src/test/java/org/traccar/handler/events/IgnitionEventHandlerTest.java b/src/test/java/org/traccar/handler/events/IgnitionEventHandlerTest.java
index 84898bea0..972932df4 100644
--- a/src/test/java/org/traccar/handler/events/IgnitionEventHandlerTest.java
+++ b/src/test/java/org/traccar/handler/events/IgnitionEventHandlerTest.java
@@ -1,6 +1,6 @@
package org.traccar.handler.events;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import org.traccar.BaseTest;
import org.traccar.model.Event;
import org.traccar.model.Position;
@@ -8,7 +8,7 @@ import org.traccar.session.cache.CacheManager;
import java.util.Map;
-import static org.junit.Assert.assertNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.Mockito.mock;
public class IgnitionEventHandlerTest extends BaseTest {
diff --git a/src/test/java/org/traccar/handler/events/MaintenanceEventHandlerTest.java b/src/test/java/org/traccar/handler/events/MaintenanceEventHandlerTest.java
index aa2d0bbe3..661336d76 100644
--- a/src/test/java/org/traccar/handler/events/MaintenanceEventHandlerTest.java
+++ b/src/test/java/org/traccar/handler/events/MaintenanceEventHandlerTest.java
@@ -1,19 +1,20 @@
package org.traccar.handler.events;
-import org.junit.Test;
+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 java.util.Set;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
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 {
@@ -29,7 +30,7 @@ public class MaintenanceEventHandlerTest extends BaseTest {
var maintenance = mock(Maintenance.class);
when(maintenance.getType()).thenReturn(Position.KEY_TOTAL_DISTANCE);
- var maintenances = Arrays.asList(maintenance);
+ var maintenances = Set.of(maintenance);
var cacheManager = mock(CacheManager.class);
when(cacheManager.getDeviceObjects(anyLong(), eq(Maintenance.class))).thenReturn(maintenances);
@@ -48,12 +49,12 @@ public class MaintenanceEventHandlerTest extends BaseTest {
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);
+ position.set(Position.KEY_TOTAL_DISTANCE, 10001);
+ assertEquals(1, eventHandler.analyzePosition(position).size());
lastPosition.set(Position.KEY_TOTAL_DISTANCE, 11999);
- position.set(Position.KEY_TOTAL_DISTANCE, 12001);
- assertTrue(eventHandler.analyzePosition(position).size() == 1);
+ position.set(Position.KEY_TOTAL_DISTANCE, 12001);
+ assertEquals(1, eventHandler.analyzePosition(position).size());
}
diff --git a/src/test/java/org/traccar/handler/events/MotionEventHandlerTest.java b/src/test/java/org/traccar/handler/events/MotionEventHandlerTest.java
index b77676dc8..c61ae913d 100644
--- a/src/test/java/org/traccar/handler/events/MotionEventHandlerTest.java
+++ b/src/test/java/org/traccar/handler/events/MotionEventHandlerTest.java
@@ -1,6 +1,6 @@
package org.traccar.handler.events;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import org.traccar.BaseTest;
import org.traccar.model.Event;
import org.traccar.model.Position;
@@ -13,8 +13,8 @@ import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
-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 MotionEventHandlerTest extends BaseTest {
@@ -36,7 +36,7 @@ public class MotionEventHandlerTest extends BaseTest {
@Test
public void testMotionWithPosition() throws ParseException {
- TripsConfig tripsConfig = new TripsConfig(500, 300000, 300000, 0, false, false, 0.01);
+ TripsConfig tripsConfig = new TripsConfig(500, 300000, 300000, 0, false);
MotionState state = new MotionState();
@@ -63,7 +63,7 @@ public class MotionEventHandlerTest extends BaseTest {
@Test
public void testMotionFluctuation() throws ParseException {
- TripsConfig tripsConfig = new TripsConfig(500, 300000, 300000, 0, false, false, 0.01);
+ TripsConfig tripsConfig = new TripsConfig(500, 300000, 300000, 0, false);
MotionState state = new MotionState();
@@ -94,7 +94,7 @@ public class MotionEventHandlerTest extends BaseTest {
@Test
public void testStopWithPositionIgnition() throws ParseException {
- TripsConfig tripsConfig = new TripsConfig(500, 300000, 300000, 0, true, false, 0.01);
+ TripsConfig tripsConfig = new TripsConfig(500, 300000, 300000, 0, true);
MotionState state = new MotionState();
state.setMotionStreak(true);
diff --git a/src/test/java/org/traccar/handler/events/OverspeedEventHandlerTest.java b/src/test/java/org/traccar/handler/events/OverspeedEventHandlerTest.java
index ee18ee052..97d929551 100644
--- a/src/test/java/org/traccar/handler/events/OverspeedEventHandlerTest.java
+++ b/src/test/java/org/traccar/handler/events/OverspeedEventHandlerTest.java
@@ -1,6 +1,6 @@
package org.traccar.handler.events;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import org.traccar.BaseTest;
import org.traccar.model.Event;
import org.traccar.model.Position;
@@ -12,9 +12,9 @@ import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
+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 {
@@ -35,14 +35,14 @@ public class OverspeedEventHandlerTest extends BaseTest {
private void testOverspeedWithPosition(long geofenceId) throws ParseException {
OverspeedState state = new OverspeedState();
- OverspeedProcessor.updateState(state, position("2017-01-01 00:00:00", 50), 40, 15000, geofenceId);
+ OverspeedProcessor.updateState(state, position("2017-01-01 00:00:00", 50), 40, 1, 15000, geofenceId);
assertNull(state.getEvent());
verifyState(state, true, geofenceId);
- OverspeedProcessor.updateState(state, position("2017-01-01 00:00:10", 55), 40, 15000, geofenceId);
+ OverspeedProcessor.updateState(state, position("2017-01-01 00:00:10", 55), 40, 1, 15000, geofenceId);
assertNull(state.getEvent());
- OverspeedProcessor.updateState(state, position("2017-01-01 00:00:20", 55), 40, 15000, geofenceId);
+ 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);
@@ -50,11 +50,11 @@ public class OverspeedEventHandlerTest extends BaseTest {
assertEquals(geofenceId, state.getEvent().getGeofenceId());
verifyState(state, true, 0);
- OverspeedProcessor.updateState(state, position("2017-01-01 00:00:30", 55), 40, 15000, geofenceId);
+ OverspeedProcessor.updateState(state, position("2017-01-01 00:00:30", 55), 40, 1, 15000, geofenceId);
assertNull(state.getEvent());
verifyState(state, true, 0);
- OverspeedProcessor.updateState(state, position("2017-01-01 00:00:30", 30), 40, 15000, geofenceId);
+ OverspeedProcessor.updateState(state, position("2017-01-01 00:00:30", 30), 40, 1, 15000, geofenceId);
assertNull(state.getEvent());
verifyState(state, false, 0);
}
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 0196cef9d..707e419ec 100644
--- a/src/test/java/org/traccar/helper/BufferUtilTest.java
+++ b/src/test/java/org/traccar/helper/BufferUtilTest.java
@@ -2,11 +2,11 @@ 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 {
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 02d42447e..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 {
diff --git a/src/test/java/org/traccar/protocol/AdmProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AdmProtocolDecoderTest.java
index 810d53bf7..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 {
diff --git a/src/test/java/org/traccar/protocol/AdmProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/AdmProtocolEncoderTest.java
index 79f37c4e4..26f59015d 100644
--- a/src/test/java/org/traccar/protocol/AdmProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/AdmProtocolEncoderTest.java
@@ -16,11 +16,11 @@
*/
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 {
diff --git a/src/test/java/org/traccar/protocol/AisProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AisProtocolDecoderTest.java
index 36ea3d361..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 {
diff --git a/src/test/java/org/traccar/protocol/AlematicsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AlematicsProtocolDecoderTest.java
index 585e02c02..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 {
diff --git a/src/test/java/org/traccar/protocol/AnytrekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AnytrekProtocolDecoderTest.java
index 5e0923e9c..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 {
diff --git a/src/test/java/org/traccar/protocol/ApelProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ApelProtocolDecoderTest.java
index 369c13115..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 {
diff --git a/src/test/java/org/traccar/protocol/AplicomFrameDecoderTest.java b/src/test/java/org/traccar/protocol/AplicomFrameDecoderTest.java
index b055221f2..316e897a7 100644
--- a/src/test/java/org/traccar/protocol/AplicomFrameDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/AplicomFrameDecoderTest.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 AplicomFrameDecoderTest extends ProtocolTest {
diff --git a/src/test/java/org/traccar/protocol/AplicomProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AplicomProtocolDecoderTest.java
index 4b25830f8..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 {
diff --git a/src/test/java/org/traccar/protocol/AppelloProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AppelloProtocolDecoderTest.java
index e821c9dba..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 {
diff --git a/src/test/java/org/traccar/protocol/AquilaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AquilaProtocolDecoderTest.java
index 38c5d3b6d..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 {
diff --git a/src/test/java/org/traccar/protocol/Ardi01ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Ardi01ProtocolDecoderTest.java
index fd65ce585..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 {
diff --git a/src/test/java/org/traccar/protocol/ArknavProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ArknavProtocolDecoderTest.java
index b07dff150..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 {
diff --git a/src/test/java/org/traccar/protocol/ArknavX8ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ArknavX8ProtocolDecoderTest.java
index 9686c694b..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 {
diff --git a/src/test/java/org/traccar/protocol/ArmoliProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ArmoliProtocolDecoderTest.java
index 515f7f7b2..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;
diff --git a/src/test/java/org/traccar/protocol/ArnaviBinaryProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ArnaviBinaryProtocolDecoderTest.java
index f96da5203..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 {
diff --git a/src/test/java/org/traccar/protocol/ArnaviFrameDecoderTest.java b/src/test/java/org/traccar/protocol/ArnaviFrameDecoderTest.java
index 92ca5d2b0..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 {
diff --git a/src/test/java/org/traccar/protocol/ArnaviTextProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ArnaviTextProtocolDecoderTest.java
index e27367119..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 {
diff --git a/src/test/java/org/traccar/protocol/AstraProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AstraProtocolDecoderTest.java
index 2d8798dff..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 {
diff --git a/src/test/java/org/traccar/protocol/At2000FrameDecoderTest.java b/src/test/java/org/traccar/protocol/At2000FrameDecoderTest.java
index 773a8f7f5..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 {
diff --git a/src/test/java/org/traccar/protocol/At2000ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/At2000ProtocolDecoderTest.java
index 8c32289f1..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 {
diff --git a/src/test/java/org/traccar/protocol/AtrackFrameDecoderTest.java b/src/test/java/org/traccar/protocol/AtrackFrameDecoderTest.java
index 958814e53..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 {
diff --git a/src/test/java/org/traccar/protocol/AtrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AtrackProtocolDecoderTest.java
index b6b09fc25..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 {
@@ -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"));
diff --git a/src/test/java/org/traccar/protocol/AuroProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AuroProtocolDecoderTest.java
index 368f7ed4c..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 {
diff --git a/src/test/java/org/traccar/protocol/AustinNbProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AustinNbProtocolDecoderTest.java
index 30152e94c..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 {
diff --git a/src/test/java/org/traccar/protocol/AutoFonProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AutoFonProtocolDecoderTest.java
index 8e17d5673..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;
diff --git a/src/test/java/org/traccar/protocol/AutoGradeProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AutoGradeProtocolDecoderTest.java
index 7f837451c..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 {
diff --git a/src/test/java/org/traccar/protocol/AutoTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AutoTrackProtocolDecoderTest.java
index 51bbd0d8c..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 {
diff --git a/src/test/java/org/traccar/protocol/AvemaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AvemaProtocolDecoderTest.java
index 7f41d6b47..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;
diff --git a/src/test/java/org/traccar/protocol/Avl301ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Avl301ProtocolDecoderTest.java
index 1da432cea..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;
diff --git a/src/test/java/org/traccar/protocol/B2316ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/B2316ProtocolDecoderTest.java
index ea3b38e7d..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 {
diff --git a/src/test/java/org/traccar/protocol/BceFrameDecoderTest.java b/src/test/java/org/traccar/protocol/BceFrameDecoderTest.java
index e5a442f2f..f25daca76 100644
--- a/src/test/java/org/traccar/protocol/BceFrameDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/BceFrameDecoderTest.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 BceFrameDecoderTest extends ProtocolTest {
diff --git a/src/test/java/org/traccar/protocol/BceProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/BceProtocolDecoderTest.java
index 1d980b7e5..fdfb721c5 100644
--- a/src/test/java/org/traccar/protocol/BceProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/BceProtocolDecoderTest.java
@@ -1,13 +1,13 @@
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 BceProtocolDecoderTest extends ProtocolTest {
- @Ignore
+ @Disabled
@Test
public void testDecodeFail() throws Exception {
diff --git a/src/test/java/org/traccar/protocol/BceProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/BceProtocolEncoderTest.java
index bfaa3ccd8..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;
diff --git a/src/test/java/org/traccar/protocol/BlackKiteProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/BlackKiteProtocolDecoderTest.java
index 2fb93f2a9..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 {
diff --git a/src/test/java/org/traccar/protocol/BlueProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/BlueProtocolDecoderTest.java
index 1de542e24..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;
diff --git a/src/test/java/org/traccar/protocol/BoxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/BoxProtocolDecoderTest.java
index 0a19eb3b0..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 {
diff --git a/src/test/java/org/traccar/protocol/BstplProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/BstplProtocolDecoderTest.java
index ae4c47229..fd3561d18 100644
--- a/src/test/java/org/traccar/protocol/BstplProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/BstplProtocolDecoderTest.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 BstplProtocolDecoderTest extends ProtocolTest {
diff --git a/src/test/java/org/traccar/protocol/C2stekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/C2stekProtocolDecoderTest.java
index 28876075d..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 {
diff --git a/src/test/java/org/traccar/protocol/CalAmpProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CalAmpProtocolDecoderTest.java
index 1a8431f23..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 {
diff --git a/src/test/java/org/traccar/protocol/CarTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CarTrackProtocolDecoderTest.java
index d12d4aa9f..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 {
diff --git a/src/test/java/org/traccar/protocol/CarscopProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CarscopProtocolDecoderTest.java
index 71137cacf..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 {
diff --git a/src/test/java/org/traccar/protocol/CastelProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CastelProtocolDecoderTest.java
index 1add623b7..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;
@@ -12,6 +12,14 @@ public class CastelProtocolDecoderTest extends ProtocolTest {
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"),
Position.KEY_RESULT, "MALC381CMKM567148");
diff --git a/src/test/java/org/traccar/protocol/CastelProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/CastelProtocolEncoderTest.java
index 9b3a285da..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;
diff --git a/src/test/java/org/traccar/protocol/CautelaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CautelaProtocolDecoderTest.java
index 4022d688b..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 {
diff --git a/src/test/java/org/traccar/protocol/CellocatorFrameDecoderTest.java b/src/test/java/org/traccar/protocol/CellocatorFrameDecoderTest.java
index c266dd1f4..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 {
diff --git a/src/test/java/org/traccar/protocol/CellocatorProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CellocatorProtocolDecoderTest.java
index 27cad39a3..01dd9f4cb 100644
--- a/src/test/java/org/traccar/protocol/CellocatorProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/CellocatorProtocolDecoderTest.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;
diff --git a/src/test/java/org/traccar/protocol/CellocatorProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/CellocatorProtocolEncoderTest.java
index bb5d4979d..4d5c8cfe4 100644
--- a/src/test/java/org/traccar/protocol/CellocatorProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/CellocatorProtocolEncoderTest.java
@@ -1,13 +1,13 @@
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 {
diff --git a/src/test/java/org/traccar/protocol/CguardProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CguardProtocolDecoderTest.java
index 835ab5fe5..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 {
diff --git a/src/test/java/org/traccar/protocol/CityeasyProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CityeasyProtocolDecoderTest.java
index 0c1fc1f5d..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;
diff --git a/src/test/java/org/traccar/protocol/CityeasyProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/CityeasyProtocolEncoderTest.java
index 6da9856c8..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;
diff --git a/src/test/java/org/traccar/protocol/ContinentalProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ContinentalProtocolDecoderTest.java
index 6ac2ae01d..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 {
diff --git a/src/test/java/org/traccar/protocol/CradlepointProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CradlepointProtocolDecoderTest.java
index f95bfb54d..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 {
diff --git a/src/test/java/org/traccar/protocol/DingtekFrameDecoderTest.java b/src/test/java/org/traccar/protocol/DingtekFrameDecoderTest.java
index c63775858..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 {
diff --git a/src/test/java/org/traccar/protocol/DingtekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DingtekProtocolDecoderTest.java
index 7e5f68e05..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 {
diff --git a/src/test/java/org/traccar/protocol/DishaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DishaProtocolDecoderTest.java
index 3faccd7ea..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 {
diff --git a/src/test/java/org/traccar/protocol/DmtHttpProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DmtHttpProtocolDecoderTest.java
index 99760546f..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 {
diff --git a/src/test/java/org/traccar/protocol/DmtProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DmtProtocolDecoderTest.java
index 9c14a1ebe..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 {
diff --git a/src/test/java/org/traccar/protocol/DolphinProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DolphinProtocolDecoderTest.java
index b9e3da67d..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 {
diff --git a/src/test/java/org/traccar/protocol/DraginoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DraginoProtocolDecoderTest.java
new file mode 100644
index 000000000..26de75141
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/DraginoProtocolDecoderTest.java
@@ -0,0 +1,20 @@
+package org.traccar.protocol;
+
+import io.netty.handler.codec.http.HttpMethod;
+import org.junit.jupiter.api.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class DraginoProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ var decoder = inject(new DraginoProtocolDecoder(null));
+
+ verifyPosition(decoder, request(HttpMethod.POST, "/",
+ buffer("{\"end_device_ids\":{\"device_id\":\"eui-a840412b81874509\",\"application_ids\":{\"application_id\":\"teast12344321\"},\"dev_eui\":\"A840412B81874509\",\"join_eui\":\"A840410000000102\",\"dev_addr\":\"260BB59A\"},\"correlation_ids\":[\"gs:uplink:01HDTGK4QWSRX0AMP8E6R1R809\"],\"received_at\":\"2023-10-28T06:47:23.853335601Z\",\"uplink_message\":{\"session_key_id\":\"AYt1CUoFjSPmZ+/Hf8bbng==\",\"f_port\":2,\"f_cnt\":1,\"frm_payload\":\"AAAAAAAAAAAPoiACpwEQ\",\"decoded_payload\":{\"ALARM_status\":\"FALSE\",\"BatV\":4.002,\"Hum\":67.9,\"LON\":\"ON\",\"Latitude\":0,\"Location\":0,\"Longitude\":0,\"MD\":0,\"Tem\":27.2},\"rx_metadata\":[{\"gateway_ids\":{\"gateway_id\":\"eui-a840411b7c4c4150\",\"eui\":\"A840411B7C4C4150\"},\"time\":\"2023-10-28T06:47:23.522309Z\",\"timestamp\":2618357987,\"rssi\":-101,\"channel_rssi\":-101,\"snr\":8.5,\"uplink_token\":\"CiIKIAoUZXVpLWE4NDA0MTFiN2M0YzQxNTASCKhAQRt8TEFQEOPxw+AJGgwI+9zyqQYQ+LGJswIguO2wkpqnNg==\",\"received_at\":\"2023-10-28T06:47:23.643979512Z\"},{\"gateway_ids\":{\"gateway_id\":\"test123123\",\"eui\":\"A840411D178C4150\"},\"time\":\"2023-10-28T06:47:23.522842Z\",\"timestamp\":3064971227,\"rssi\":-78,\"channel_rssi\":-78,\"snr\":8.8,\"uplink_token\":\"ChgKFgoKdGVzdDEyMzEyMxIIqEBBHReMQVAQ2/++tQsaDAj73PKpBhDyj+e1AiD43pX0ma44\",\"received_at\":\"2023-10-28T06:47:23.649709554Z\"},{\"gateway_ids\":{\"gateway_id\":\"eui-b8ea26fdfe2d5d28\",\"eui\":\"B8EA26FDFE2D5D28\"},\"time\":\"2023-10-28T06:47:23.536703Z\",\"timestamp\":2967400725,\"rssi\":-38,\"channel_rssi\":-38,\"snr\":14.2,\"frequency_offset\":\"1061\",\"uplink_token\":\"CiIKIAoUZXVpLWI4ZWEyNmZkZmUyZDVkMjgSCLjqJv3+LV0oEJXi+4YLGgwI+9zyqQYQxqentwIgiPT2tq60NQ==\",\"received_at\":\"2023-10-28T06:47:23.532394512Z\"},{\"gateway_ids\":{\"gateway_id\":\"eu868-1\",\"eui\":\"A840411B7E5E1868\"},\"time\":\"2023-10-28T06:47:23.526195Z\",\"timestamp\":3486064627,\"rssi\":-35,\"channel_rssi\":-35,\"snr\":9,\"uplink_token\":\"ChUKEwoHZXU4NjgtMRIIqEBBG35eGGgQ87+k/gwaDAj73PKpBhDZzuW4AiC4mpPNusM1\",\"received_at\":\"2023-10-28T06:47:23.537018603Z\"}],\"settings\":{\"data_rate\":{\"lora\":{\"bandwidth\":125000,\"spreading_factor\":7,\"coding_rate\":\"4/5\"}},\"frequency\":\"868100000\",\"timestamp\":2618357987,\"time\":\"2023-10-28T06:47:23.522309Z\"},\"received_at\":\"2023-10-28T06:47:23.644805734Z\",\"consumed_airtime\":\"0.066816s\",\"network_ids\":{\"net_id\":\"000013\",\"ns_id\":\"EC656E0000000181\",\"tenant_id\":\"ttn\",\"cluster_id\":\"eu1\",\"cluster_address\":\"eu1.cloud.thethings.network\"}}}")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Dsf22FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Dsf22FrameDecoderTest.java
index 7e3ec0706..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 {
diff --git a/src/test/java/org/traccar/protocol/Dsf22ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Dsf22ProtocolDecoderTest.java
index 2e52b950d..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 {
diff --git a/src/test/java/org/traccar/protocol/DualcamFrameDecoderTest.java b/src/test/java/org/traccar/protocol/DualcamFrameDecoderTest.java
index f74a40d13..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 {
diff --git a/src/test/java/org/traccar/protocol/DualcamProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DualcamProtocolDecoderTest.java
index a61f20c13..e1c31fc47 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 {
@@ -14,6 +14,9 @@ public class DualcamProtocolDecoderTest extends ProtocolTest {
"000000050001403a4abaa31444000400"));
verifyNull(decoder, binary(
+ "000d001e64736d2f706963747572652f3233313032302f3233313435322e6a706700"));
+
+ verifyNull(decoder, binary(
"00010006000000110000"));
verifyNull(decoder, binary(
diff --git a/src/test/java/org/traccar/protocol/DwayProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DwayProtocolDecoderTest.java
index 1cdd82664..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 {
diff --git a/src/test/java/org/traccar/protocol/EasyTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EasyTrackProtocolDecoderTest.java
index ef319449e..d40463c1e 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 {
@@ -10,6 +10,9 @@ public class EasyTrackProtocolDecoderTest extends ProtocolTest {
var decoder = inject(new EasyTrackProtocolDecoder(null));
+ verifyAttributes(decoder, text(
+ "*ET,358999999999916,OB,BD$V14.2;R08258;S166;P058.4;O079.2;C025;L081.5;XM091.393;M722379;F352.956;T0037184;A01;B00;D00;GX3;GY-6;GZ-268;@4#"));
+
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 1e1934435..a2a720263 100644
--- a/src/test/java/org/traccar/protocol/EasyTrackProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/EasyTrackProtocolEncoderTest.java
@@ -1,10 +1,10 @@
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 {
diff --git a/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java
index ec1467c09..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;
diff --git a/src/test/java/org/traccar/protocol/EelinkProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/EelinkProtocolEncoderTest.java
index 380888843..300a1c5b6 100644
--- a/src/test/java/org/traccar/protocol/EelinkProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/EelinkProtocolEncoderTest.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;
diff --git a/src/test/java/org/traccar/protocol/EgtsFrameDecoderTest.java b/src/test/java/org/traccar/protocol/EgtsFrameDecoderTest.java
index dde71d1e7..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 {
diff --git a/src/test/java/org/traccar/protocol/EgtsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EgtsProtocolDecoderTest.java
index 0f5a40605..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 {
diff --git a/src/test/java/org/traccar/protocol/EnforaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EnforaProtocolDecoderTest.java
index 2d2a211c3..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 {
diff --git a/src/test/java/org/traccar/protocol/EnnfuProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EnnfuProtocolDecoderTest.java
index 0134e8052..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 {
diff --git a/src/test/java/org/traccar/protocol/EnvotechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EnvotechProtocolDecoderTest.java
index 50db6c743..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 {
diff --git a/src/test/java/org/traccar/protocol/EsealProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EsealProtocolDecoderTest.java
index 2d9b19b70..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 {
diff --git a/src/test/java/org/traccar/protocol/EsealProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/EsealProtocolEncoderTest.java
index 3ea8de5d6..e87a56b67 100644
--- a/src/test/java/org/traccar/protocol/EsealProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/EsealProtocolEncoderTest.java
@@ -1,10 +1,10 @@
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 {
diff --git a/src/test/java/org/traccar/protocol/EskyFrameDecoderTest.java b/src/test/java/org/traccar/protocol/EskyFrameDecoderTest.java
index f808cd4a8..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 {
diff --git a/src/test/java/org/traccar/protocol/EskyProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EskyProtocolDecoderTest.java
index 678007f5c..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;
diff --git a/src/test/java/org/traccar/protocol/ExtremTracProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ExtremTracProtocolDecoderTest.java
index 36a26bbe3..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 {
diff --git a/src/test/java/org/traccar/protocol/FifotrackFrameDecoderTest.java b/src/test/java/org/traccar/protocol/FifotrackFrameDecoderTest.java
index 417c49de5..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 {
diff --git a/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java
index ec08b432c..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;
@@ -11,6 +11,13 @@ public class FifotrackProtocolDecoderTest extends ProtocolTest {
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 c3352f510..643c9202d 100644
--- a/src/test/java/org/traccar/protocol/FifotrackProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/FifotrackProtocolEncoderTest.java
@@ -1,10 +1,10 @@
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 {
diff --git a/src/test/java/org/traccar/protocol/FleetGuideProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FleetGuideProtocolDecoderTest.java
new file mode 100644
index 000000000..3cd74766d
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/FleetGuideProtocolDecoderTest.java
@@ -0,0 +1,33 @@
+package org.traccar.protocol;
+
+import org.junit.jupiter.api.Test;
+import org.traccar.ProtocolTest;
+
+public class FleetGuideProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ var decoder = inject(new FleetGuideProtocolDecoder(null));
+
+ verifyNull(decoder, binary(
+ "5300188f8a020001f36a"));
+
+ verifyPositions(decoder, false, binary(
+ "5322188f8a0200140700f355831a83ec06030c008065052c06ffffffff033006808001180003200100005ec0"));
+
+ verifyPositions(decoder, binary(
+ "539e598f8a020003020700e378351ac39f040c04ffa92e806a28a13b1f00b638030c000067052c06ffffffff033006808001180003200100000700e478351ac40204303dab2e80cb27a13b1c00ac40021b30e778351ac502043082a72e8054020530bf30021b30e978351ac69f0d0c0433a72e80c123a13b2000df3807002579351ac79f06020a170000df28021b476179351ac8020f30021c81279d79351ac9020f30021c8207d979351aca020f30021c8157157a351acb022b8140517a351acc022b608d7a351acd022b60c97a351ace022b30057b351acf022b30417b351ad0022b307d7b351ad1020f3048021b30b97b351ad2020f3070030c004066021630f57b351ad30213841080021730317c351ad4021330c00217306d7c351ad5020f3050021b30a97c351ad602138170021830e57c351ad7020f3058021b30217d351ad8021385500218305d7d351ad9022b8110997d351ada022b8170d57d351adb022b30117e351adc020f3068030ce184680216304d7e351add020f3060030ce34469021630897e351ade021260e4021830c57e351adf021230e5021830017f351ae002293022f2"));
+
+ verifyPositions(decoder, binary(
+ "538958235a02008408070027398f1a7da3040c0485c7437f9db8a635ca016189030c76a567052c06bd05ffff033006138009340f69fb0080008000800118010320018005070029398f1a7e08043b39f0437f14b9a635cb010288030c7108233b2b398f1a7f08043bec18447f7fbca6357c010b8008263b2d398f1a8008043be53d447f61c0a63562011480030c7708213b6f3b"));
+
+ verifyPositions(decoder, binary(
+ "53361886b60e00540700cf3b8f1a838d060e041dc683a1b672a04b0000ba885d00030c0ea567052c06ffffffff03300680800118000720070000000000006bc3"));
+
+ verifyPositions(decoder, binary(
+ "53dd581fc10e0094080700373d8f1a5e2f090e04cbb6c2a3d3078f4b5d023090b500030cac6567052c06ffffffff0330068080054810fdfdfdff0548121bf0000005481321d4830005481454cf08040e15eee20100054816770308040e171c0208040718281f0804071984000804071a583a000001180107200781050000000007003b3d8f1a5f2f040e04dcfbc2a3b15a8f4b67023890b4081c7c1f08067c2e08067c61081b7c842008057c8308067c443c08107c3e3d8f1a6008047cf125c3a31ca48f4b7c0242081e7c2208067c3908067c6b08067cef08147cac21080c7ce03e080e7cbb62"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/FlespiProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FlespiProtocolDecoderTest.java
index c794c9094..79131c483 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 {
@@ -11,14 +11,15 @@ public class FlespiProtocolDecoderTest extends ProtocolTest {
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}]")));
+ 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}]")));
- verifyPositions(decoder, request(HttpMethod.POST, "/",
- buffer("[{\"geofence.inside.status\":false,\"position.valid\":false,\"ain#4\":0,\"rs232.sensor.value#1\":0,\"position.direction\":0,\"rs232.sensor.value#0\":0,\"position.speed\":0,\"position.latitude\":10.11223,\"refrigerator.sensor.temperature#1\":62.5,\"gnss.antenna.cut.status\":true,\"din\":3,\"ain#3\":0,\"refrigerator.sensor.temperature#3\":71.4,\"position.altitude\":0,\"shock.event.trigger\":false,\"alarm.mode.status\":false,\"ibutton.event.connect\":false,\"refrigerator.sensor.temperature#4\":66.7,\"internal.battery.voltage.limit.lower.status\":false,\"ain#2\":0,\"gsm.signal.level\":0,\"refrigerator.connection.status\":0,\"position.satellites\":0,\"external.powersource.voltage.range.outside.status\":false,\"refrigerator.sensor.temperature#2\":68.2,\"incline.event.trigger\":false,\"alarm.event.trigger\":false,\"movement.status\":true,\"refrigerator.sensor.temperature#6\":68.9,\"ident\":\"605630\",\"timestamp\":946684840,\"engine.ignition.status\":true,\"gsm.sim.status\":true,\"record.seqnum\":8165,\"external.powersource.voltage\":15.298,\"gnss.enum\":\"glonass\",\"position.longitude\":20.88774,\"battery.voltage\":4.088,\"refrigerator.sensor.temperature#5\":71.3,\"ain#1\":0,\"internal.bus.supply.voltage.range.outside.status\":false},{\"geofence.inside.status\":false,\"position.valid\":true,\"ain#4\":0,\"rs232.sensor.value#1\":0,\"position.direction\":0,\"rs232.sensor.value#0\":0,\"position.speed\":0,\"position.latitude\":57.986744,\"refrigerator.sensor.temperature#1\":74.1,\"gnss.antenna.cut.status\":false,\"ain#3\":0,\"position.hdop\":21.1,\"refrigerator.sensor.temperature#3\":71.4,\"position.altitude\":219,\"shock.event.trigger\":false,\"alarm.mode.status\":false,\"ibutton.event.connect\":false,\"refrigerator.sensor.temperature#4\":70.5,\"internal.battery.voltage.limit.lower.status\":false,\"ain#2\":0,\"gsm.signal.level\":0,\"refrigerator.connection.status\":0,\"position.satellites\":5,\"external.powersource.voltage.range.outside.status\":false,\"refrigerator.sensor.temperature#2\":71.3,\"incline.event.trigger\":false,\"alarm.event.trigger\":false,\"movement.status\":true,\"refrigerator.sensor.temperature#6\":69.3,\"ident\":\"605630\",\"timestamp\":1392272112,\"engine.ignition.status\":true,\"gsm.sim.status\":true,\"record.seqnum\":8174,\"external.powersource.voltage\":15.303,\"gnss.enum\":\"glonass\",\"position.longitude\":56.207576,\"battery.voltage\":3.934,\"refrigerator.sensor.temperature#5\":68.1,\"ain#1\":0,\"internal.bus.supply.voltage.range.outside.status\":false}]")));
+ verifyPositions(decoder, request(HttpMethod.POST, "/", buffer(
+ "[{\"geofence.inside.status\":false,\"position.valid\":false,\"ain#4\":0,\"rs232.sensor.value#1\":0,\"position.direction\":0,\"rs232.sensor.value#0\":0,\"position.speed\":0,\"position.latitude\":10.11223,\"refrigerator.sensor.temperature#1\":62.5,\"gnss.antenna.cut.status\":true,\"din\":3,\"ain#3\":0,\"refrigerator.sensor.temperature#3\":71.4,\"position.altitude\":0,\"shock.event.trigger\":false,\"alarm.mode.status\":false,\"ibutton.event.connect\":false,\"refrigerator.sensor.temperature#4\":66.7,\"internal.battery.voltage.limit.lower.status\":false,\"ain#2\":0,\"gsm.signal.level\":0,\"refrigerator.connection.status\":0,\"position.satellites\":0,\"external.powersource.voltage.range.outside.status\":false,\"refrigerator.sensor.temperature#2\":68.2,\"incline.event.trigger\":false,\"alarm.event.trigger\":false,\"movement.status\":true,\"refrigerator.sensor.temperature#6\":68.9,\"ident\":\"605630\",\"timestamp\":946684840,\"engine.ignition.status\":true,\"gsm.sim.status\":true,\"record.seqnum\":8165,\"external.powersource.voltage\":15.298,\"gnss.enum\":\"glonass\",\"position.longitude\":20.88774,\"battery.voltage\":4.088,\"refrigerator.sensor.temperature#5\":71.3,\"ain#1\":0,\"internal.bus.supply.voltage.range.outside.status\":false},{\"geofence.inside.status\":false,\"position.valid\":true,\"ain#4\":0,\"rs232.sensor.value#1\":0,\"position.direction\":0,\"rs232.sensor.value#0\":0,\"position.speed\":0,\"position.latitude\":57.986744,\"refrigerator.sensor.temperature#1\":74.1,\"gnss.antenna.cut.status\":false,\"ain#3\":0,\"position.hdop\":21.1,\"refrigerator.sensor.temperature#3\":71.4,\"position.altitude\":219,\"shock.event.trigger\":false,\"alarm.mode.status\":false,\"ibutton.event.connect\":false,\"refrigerator.sensor.temperature#4\":70.5,\"internal.battery.voltage.limit.lower.status\":false,\"ain#2\":0,\"gsm.signal.level\":0,\"refrigerator.connection.status\":0,\"position.satellites\":5,\"external.powersource.voltage.range.outside.status\":false,\"refrigerator.sensor.temperature#2\":71.3,\"incline.event.trigger\":false,\"alarm.event.trigger\":false,\"movement.status\":true,\"refrigerator.sensor.temperature#6\":69.3,\"ident\":\"605630\",\"timestamp\":1392272112,\"engine.ignition.status\":true,\"gsm.sim.status\":true,\"record.seqnum\":8174,\"external.powersource.voltage\":15.303,\"gnss.enum\":\"glonass\",\"position.longitude\":56.207576,\"battery.voltage\":3.934,\"refrigerator.sensor.temperature#5\":68.1,\"ain#1\":0,\"internal.bus.supply.voltage.range.outside.status\":false}]")));
+
+ verifyPositions(decoder, request(HttpMethod.POST, "/", buffer(
+ "[{\"ain#1\":1,\"ain#2\":0,\"ain#3\":0,\"ain#4\":0,\"alarm.event.trigger\":true,\"custom.SOS\":1,\"custom.dparam\":3.141593,\"custom.ign\":1,\"custom.iparam\":-55,\"custom.tparam\":\"lorem\",\"din\":722,\"dout\":1048576,\"ident\":\"namo:namo\",\"position.altitude\":300,\"position.direction\":0,\"position.hdop\":1.1,\"position.latitude\":53.90821,\"position.longitude\":27.524165,\"position.satellites\":7,\"position.speed\":0,\"timestamp\":1508508510.013227}]")));
- verifyPositions(decoder, request(HttpMethod.POST, "/",
- buffer("[{\"ain#1\":1,\"ain#2\":0,\"ain#3\":0,\"ain#4\":0,\"alarm.event.trigger\":true,\"custom.SOS\":1,\"custom.dparam\":3.141593,\"custom.ign\":1,\"custom.iparam\":-55,\"custom.tparam\":\"lorem\",\"din\":722,\"dout\":1048576,\"ident\":\"namo:namo\",\"position.altitude\":300,\"position.direction\":0,\"position.hdop\":1.1,\"position.latitude\":53.90821,\"position.longitude\":27.524165,\"position.satellites\":7,\"position.speed\":0,\"timestamp\":1508508510.013227}]")));
}
-} \ No newline at end of file
+}
diff --git a/src/test/java/org/traccar/protocol/FlexApiProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FlexApiProtocolDecoderTest.java
index ffb8eb3a9..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 {
diff --git a/src/test/java/org/traccar/protocol/FlexCommProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FlexCommProtocolDecoderTest.java
index ed44fc62e..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 {
diff --git a/src/test/java/org/traccar/protocol/FlexibleReportProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FlexibleReportProtocolDecoderTest.java
index 6b289396d..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 {
diff --git a/src/test/java/org/traccar/protocol/FlextrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FlextrackProtocolDecoderTest.java
index 6e5ebf4cf..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;
diff --git a/src/test/java/org/traccar/protocol/FoxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FoxProtocolDecoderTest.java
index 8e62c878e..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 {
diff --git a/src/test/java/org/traccar/protocol/FreedomProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FreedomProtocolDecoderTest.java
index 1163b4e04..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 {
diff --git a/src/test/java/org/traccar/protocol/FreematicsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FreematicsProtocolDecoderTest.java
index a7ce042e5..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 {
@@ -11,6 +11,9 @@ public class FreematicsProtocolDecoderTest extends ProtocolTest {
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"));
verifyNull(decoder, text(
diff --git a/src/test/java/org/traccar/protocol/FutureWayFrameDecoderTest.java b/src/test/java/org/traccar/protocol/FutureWayFrameDecoderTest.java
index a101ef45d..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 {
diff --git a/src/test/java/org/traccar/protocol/FutureWayProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FutureWayProtocolDecoderTest.java
index e529d5c90..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 {
diff --git a/src/test/java/org/traccar/protocol/G1rusProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/G1rusProtocolDecoderTest.java
index c8b4e35fe..059fc21df 100644
--- a/src/test/java/org/traccar/protocol/G1rusProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/G1rusProtocolDecoderTest.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 G1rusProtocolDecoderTest extends ProtocolTest {
diff --git a/src/test/java/org/traccar/protocol/GalileoFrameDecoderTest.java b/src/test/java/org/traccar/protocol/GalileoFrameDecoderTest.java
index a5aba3513..530f0930e 100644
--- a/src/test/java/org/traccar/protocol/GalileoFrameDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/GalileoFrameDecoderTest.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 GalileoFrameDecoderTest extends ProtocolTest {
@@ -13,6 +13,10 @@ public class GalileoFrameDecoderTest extends ProtocolTest {
var decoder = inject(new GalileoFrameDecoder());
assertEquals(
+ binary("01003f01001c475b166133303035333430363431383437393000001d000064897bb003000b0221c20512a60a0000000802000f209d7b8964300f2536fbfd103c1d01"),
+ decoder.decode(null, null, binary("01003f01001c475b166133303035333430363431383437393000001d000064897bb003000b0221c20512a60a0000000802000f209d7b8964300f2536fbfd103c1d01")));
+
+ assertEquals(
binary("011780011102e603383633353931303238393630323437043200801c"),
decoder.decode(null, null, binary("011780011102e603383633353931303238393630323437043200801c")));
diff --git a/src/test/java/org/traccar/protocol/GalileoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GalileoProtocolDecoderTest.java
index 7addc8e75..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 {
diff --git a/src/test/java/org/traccar/protocol/GalileoProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/GalileoProtocolEncoderTest.java
index 29fe4cc94..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;
diff --git a/src/test/java/org/traccar/protocol/GatorProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GatorProtocolDecoderTest.java
index bfb33de22..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 {
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 67a730f97..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 {
diff --git a/src/test/java/org/traccar/protocol/Gl100ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gl100ProtocolDecoderTest.java
index 31ae1d5e8..3701fa772 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 {
@@ -12,6 +12,9 @@ public class Gl100ProtocolDecoderTest extends ProtocolTest {
var decoder = inject(new Gl100ProtocolDecoder(null));
verifyPosition(decoder, text(
+ "+RESP:GTRTL,359464032011616,1,0,0,0,0.1,0,1662.5,,36.822301,-1.309476,20230706032920,0639,0002,08DF,1F5E,00,095,0101050105,4470"));
+
+ 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"));
verifyPosition(decoder, text(
diff --git a/src/test/java/org/traccar/protocol/Gl200BinaryProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gl200BinaryProtocolDecoderTest.java
index cad70c35b..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 {
diff --git a/src/test/java/org/traccar/protocol/Gl200FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Gl200FrameDecoderTest.java
index 51816222d..6fd3f0aaa 100644
--- a/src/test/java/org/traccar/protocol/Gl200FrameDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/Gl200FrameDecoderTest.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 Gl200FrameDecoderTest extends ProtocolTest {
diff --git a/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java
index f3a77228c..b6e50e07d 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;
@@ -11,8 +11,55 @@ public class Gl200TextProtocolDecoderTest extends ProtocolTest {
var decoder = inject(new Gl200TextProtocolDecoder(null));
+ verifyPositions(decoder, buffer(
+ "+RESP:GTERI,C2010D,869653060009939,GV600M,00000100,,10,1,0,0.0,0,163.9,10.239379,45.931389,20231210233723,0222,0010,2F31,006D7621,,0.0,,,,3,410000,,1,0,6,4,0,,01BF,Sondaporte,EA7D0A3882F6,1,3484,21.81,43,00,00,0,0,0,20240114221802,1875$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTERI,8020050605,867488060225819,GV355CEU,00000106,12390,10,1,1,0.0,37,253.0,10.240915,45.930262,20240226074452,0222,0010,2F31,0063952B,03,7,0,0.0,,,,,100,110000,0,0,1,FFFFF,VR3USHNSSNJ711765,0,H311796,109.40,0,,,,,,,46.54,29.91,16.63,12.88,,8080,,,00,0,20240226074453,2548$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTCAN,F10413,862599050467479,GV350M,0,1,C18CFDFF,3BKHHZ8X7GF723380,2,H2898058,232160.50,896,0,64,L/H1.5,P99.20,0,40532.95,,,,,,,,,,,1,0.0,234,2812.2,-78.508807,-0.218812,20240226211921,,,,,,20240226211922,DE03$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,8020040305,866314060272661,,,50,1,1,0.0,0,2957.9,-78.691727,-0.951205,20231227162916,,,,,00,0.0,,,,,100,210100,,,,20231227162916,0117$"));
+
+ verifyPositions(decoder, buffer(
+ "+BUFF:GTFRI,8020040200,866314060249032,,12194,10,1,3,0.0,0,20.1,-71.596533,-33.524718,20230926200338,0730,0001,772A,052B253E,02,0,0.0,,,,,0,420000,,,,20230926200340,1549$"));
+
+ verifyAttribute(decoder, buffer(
+ "+RESP:GTFRI,423037,866884047716519,GT501,0,1,1,5,12,0.1,0,46.8,-95.559173,30.109955,20231110185836,6,0e36c9916485,-50,,,,e831cd5eb79d,-73,,,,ccf4110c4bd5,-79,,,,acdb48973168,-79,,,,80ab4dc323c4,-82,,,,ec8eb5cfa1c6,-89,,,,310,10,711D,81ECF0F,00,,93,20231110185839,0005$"),
+ Position.KEY_BATTERY_LEVEL, 93);
+
+ verifyPositions(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,GV75W,00000001,,10,1,1,0.0,0,3027.8,-78.706612,-0.955699,20230418170736,0740,0002,A08C,2AB72D,00,0.0,,,,100,110000,1,0099,20230418171004,8B98$"),
+ 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$"),
+ "+RESP:GTERI,271002,863457051562823,GV300,00000002,,10,1,1,0.0,15,28.2,-58.695253,-34.625413,20230119193305,0722,0007,1168,16B3BB,00,0.0,,,,99,210100,2,1,28F8A149F69A3C25,1,0190,20230119193314,07C7$"),
Position.PREFIX_TEMP + 1, 25.0);
verifyAttribute(decoder, buffer(
@@ -129,7 +176,7 @@ public class Gl200TextProtocolDecoderTest extends ProtocolTest {
"+RESP:GTERI,310603,863286023345490,,00000002,,10,1,2,0.3,0,155.7,8.000000,52.000000,20171215213040,0262,0002,1450,9F13,00,1130.3,00539:27:19,,,110000,2,1,28FFD5239115034E,1,,20171215213041,27C7$"));
verifyPositions(decoder, buffer(
- "+RESP:GTERI,250C02,868789023691057,,00000019,,10,1,1,0.0,196,2258.0,-99.201807,19.559242,20180214002957,0334,0003,235B,7F8D,00,6786.7,,,,100,110000,1,0394,1,4,100.0,100.0,20180214003006,C72B$"));
+ "+RESP:GTERI,250C02,868789023691057,GV300,00000019,,10,1,1,0.0,196,2258.0,-99.201807,19.559242,20180214002957,0334,0003,235B,7F8D,00,6786.7,,,,100,110000,1,0394,1,4,100.0,100.0,20180214003006,C72B$"));
verifyAttributes(decoder, buffer(
"+RESP:GTCAN,310603,863286023335723,gv65,00,1,C03FFFFF,,0,,719601.00,,,,,,,,274.99,179.02,95.98,84761.00,,,0,,0,,,0,0.0,216,29.8,-2.155296,51.899400,20180209172714,0234,0010,53F3,8D38,00,20180211002128,E94E$"));
@@ -156,7 +203,7 @@ public class Gl200TextProtocolDecoderTest extends ProtocolTest {
"+RESP:GTOBD,360701,864251020253807,LSGTC58UX7Y067312,GV500,0,70FFFF,LSGTC58UX7Y067312,1,12309,983A8140,0,0,33,nan,,0,0,0,,10,0,,0,4.4,0,83.7,36.235142,49.967324,20170829112348,0255,0001,2760,9017,00,690.1,20170829112400,3456$"));
verifyPositions(decoder, buffer(
- "+RESP:GTERI,060502,861074023620928,,00000002,27822,10,1,1,0.0,84,2870.9,-78.531796,-0.277329,20170825045344,,,,,,0.0,01138:30:24,,,83,220104,2,1,28FF2776A2150308,1,FFAD,0,20170825045348,A88C$"));
+ "+RESP:GTERI,060502,861074023620928,GV300,00000002,27822,10,1,1,0.0,84,2870.9,-78.531796,-0.277329,20170825045344,,,,,,0.0,01138:30:24,,,83,220104,2,1,28FF2776A2150308,1,FFAD,0,20170825045348,A88C$"));
verifyAttributes(decoder, buffer(
"+RESP:GTINF,280500,A1000043D20139,GL300VC,41,,31,0,0,,,3.87,0,1,1,,,20170802150751,70,,48.0,,,20170802112145,03AC$"));
@@ -171,7 +218,7 @@ public class Gl200TextProtocolDecoderTest extends ProtocolTest {
"+RESP:GTTRI,862370030005908,1,0,99,1,0.0,354,18.5,18.821100,-34.084002,20170607152024,0655,0001,00DD,1CAE,00,0103010100,20170607172115,3E7D$"));
verifyPositions(decoder, buffer(
- "+RESP:GTERI,060800,861074023677175,,00000002,12351,10,1,1,0.0,0,2862.4,-78.467273,-0.164998,20170529181717,,,,,,0.0,00259:11:50,,,0,210104,2,1,28E17436060000E2,1,015F,0,20170529181723,2824$"));
+ "+RESP:GTERI,060800,861074023677175,GV300,00000002,12351,10,1,1,0.0,0,2862.4,-78.467273,-0.164998,20170529181717,,,,,,0.0,00259:11:50,,,0,210104,2,1,28E17436060000E2,1,015F,0,20170529181723,2824$"));
verifyPosition(decoder, buffer(
"+RESP:GTSWG,110100,358688000000158,,1,0,2.1,0,27.1,121.390717,31.164424,20110901073917,0460,0000,1878,0873,,20110901154653,0015$"));
@@ -192,7 +239,7 @@ public class Gl200TextProtocolDecoderTest extends ProtocolTest {
"+RESP:GTFRI,060228,862894020180553,,14827,10,1,1,3.4,199,409.6,-63.174466,-17.739317,20170407121823,0000,0000,0000,0000,00,15989.5,01070:43:13,13,180,0,220101,,,,20170407081824,9607$"));
verifyPositions(decoder, buffer(
- "+RESP:GTERI,060502,861074023376992,,00000002,27239,10,1,1,0.2,312,183.3,-79.320820,-2.499110,20170401212005,0740,0000,EE4E,C98F,00,0.0,02114:36:35,,,90,220504,2,0,0,20170401212007,9E3D$"));
+ "+RESP:GTERI,060502,861074023376992,GV300,00000002,27239,10,1,1,0.2,312,183.3,-79.320820,-2.499110,20170401212005,0740,0000,EE4E,C98F,00,0.0,02114:36:35,,,90,220504,2,0,0,20170401212007,9E3D$"));
verifyPositions(decoder, buffer(
"+RESP:GTFRI,060502,861074023689626,,25202,10,1,1,0.0,0,2744.1,-78.261047,0.023452,20170401211940,,,,,,0.0,00079:19:15,,,51,110000,,,,20170401212003,4DA7$"));
@@ -204,7 +251,7 @@ public class Gl200TextProtocolDecoderTest extends ProtocolTest {
"+RESP:GTERI,06020B,862170010196747,,00000000,,10,1,2,1.8,0,-2.5,117.198440,31.845219,20120802061037,0460,0000,5663,0358,00,0.0,,,,0,410000,20120802061040,0012$"));
verifyPositions(decoder, buffer(
- "+RESP:GTERI,060502,861074023692562,,00000002,14197,10,1,1,0.2,220,491.8,-79.064212,-2.159754,20170401212007,0740,0000,EE49,CE25,00,0.0,01509:10:58,,,87,220104,2,0,0,20170401212010,D14D$"));
+ "+RESP:GTERI,060502,861074023692562,GV300,00000002,14197,10,1,1,0.2,220,491.8,-79.064212,-2.159754,20170401212007,0740,0000,EE49,CE25,00,0.0,01509:10:58,,,87,220104,2,0,0,20170401212010,D14D$"));
verifyPositions(decoder, buffer(
"+RESP:GTFRI,210102,354524044925825,,1,1,1,29,2.8,0,133.7,-90.203063,32.265473,20170318005208,,,,,10800,4,20170318005208,0002$"));
@@ -440,6 +487,11 @@ public class Gl200TextProtocolDecoderTest extends ProtocolTest {
verifyAttributes(decoder, buffer(
"+ACK:GTGEO,1A0102,135790246811220,,0,0008,20100310172830,11F0"));
+ decoder.setModelOverride("GV355CEU");
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTCAN,8020050605,867488060270575,,00,1,FFFFFFFF,8LBETF3W4N0001613,,,22.54,0,,,,,,,7.84,4.61,3.24,3.33,,8080,,,00,0.00,0.00,1,14,14,2371,0,001FFFFF,,,,,,,,,7158,9998,0,7.84,0.00,0.00,558,,,,,,,C0,,,,,0,0.0,346,2848.5,-78.592371,-0.968132,20240202083437,0740,0002,526C,00AE7907,00,20240202083440,3F6D$"));
+
}
}
diff --git a/src/test/java/org/traccar/protocol/GlobalSatProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GlobalSatProtocolDecoderTest.java
index e244a835d..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 {
diff --git a/src/test/java/org/traccar/protocol/GlobalSatProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/GlobalSatProtocolEncoderTest.java
index 9001c4bf3..dc00cad44 100644
--- a/src/test/java/org/traccar/protocol/GlobalSatProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/GlobalSatProtocolEncoderTest.java
@@ -1,10 +1,10 @@
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 {
diff --git a/src/test/java/org/traccar/protocol/GlobalstarProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GlobalstarProtocolDecoderTest.java
index 730d4bb60..995fffad0 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 {
@@ -11,6 +11,8 @@ public class GlobalstarProtocolDecoderTest extends ProtocolTest {
var decoder = inject(new GlobalstarProtocolDecoder(null));
+ decoder.setAlternative(true);
+
verifyNull(decoder, request(HttpMethod.POST, "/", buffer(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
"<stuMessages xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"http://cody.glpconnect.com/XSD/StuMessage_Rev1_0_1.xsd\" timeStamp=\"16/09/2020 01:33:07 GMT\" messageID=\"567207180ae9100687cef8c81978371a\">\n",
@@ -22,6 +24,8 @@ public class GlobalstarProtocolDecoderTest extends ProtocolTest {
"</stuMessage>\n",
"</stuMessages>")));
+ decoder.setAlternative(false);
+
verifyPositions(decoder, request(HttpMethod.POST, "/", buffer(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
"<stuMessages xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"http://cody.glpconnect.com/XSD/StuMessage_Rev1_0_1.xsd\" timeStamp=\"25/03/2020 03:02:32 GMT\" messageID=\"300421a0fd2a100585bdde409d6f601a\">",
diff --git a/src/test/java/org/traccar/protocol/GnxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GnxProtocolDecoderTest.java
index 592c2de91..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 {
diff --git a/src/test/java/org/traccar/protocol/GoSafeProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GoSafeProtocolDecoderTest.java
index 71ffbb587..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;
@@ -14,6 +14,9 @@ public class GoSafeProtocolDecoderTest extends ProtocolTest {
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 a6e3f2d69..aea6e7592 100644
--- a/src/test/java/org/traccar/protocol/GotopProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/GotopProtocolDecoderTest.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;
diff --git a/src/test/java/org/traccar/protocol/Gps056FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Gps056FrameDecoderTest.java
index cb75b4035..332423645 100644
--- a/src/test/java/org/traccar/protocol/Gps056FrameDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/Gps056FrameDecoderTest.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 Gps056FrameDecoderTest extends ProtocolTest {
diff --git a/src/test/java/org/traccar/protocol/Gps056ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gps056ProtocolDecoderTest.java
index ac3738644..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 {
diff --git a/src/test/java/org/traccar/protocol/Gps103ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gps103ProtocolDecoderTest.java
index cf5786d75..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;
diff --git a/src/test/java/org/traccar/protocol/Gps103ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Gps103ProtocolEncoderTest.java
index f6cbe6d17..3df64039c 100644
--- a/src/test/java/org/traccar/protocol/Gps103ProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/Gps103ProtocolEncoderTest.java
@@ -1,10 +1,10 @@
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 {
diff --git a/src/test/java/org/traccar/protocol/GpsGateProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GpsGateProtocolDecoderTest.java
index a1f81b329..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 {
diff --git a/src/test/java/org/traccar/protocol/GpsMarkerProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GpsMarkerProtocolDecoderTest.java
index bc7910779..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;
diff --git a/src/test/java/org/traccar/protocol/GpsmtaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GpsmtaProtocolDecoderTest.java
index d1326515f..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 {
diff --git a/src/test/java/org/traccar/protocol/GranitFrameDecoderTest.java b/src/test/java/org/traccar/protocol/GranitFrameDecoderTest.java
index a61e708f7..9d6c189f6 100644
--- a/src/test/java/org/traccar/protocol/GranitFrameDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/GranitFrameDecoderTest.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 GranitFrameDecoderTest extends ProtocolTest {
diff --git a/src/test/java/org/traccar/protocol/GranitProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GranitProtocolDecoderTest.java
index d2e181e09..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 {
diff --git a/src/test/java/org/traccar/protocol/Gs100ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gs100ProtocolDecoderTest.java
index ce2768448..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 {
diff --git a/src/test/java/org/traccar/protocol/Gt02ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gt02ProtocolDecoderTest.java
index 25f59a948..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 {
diff --git a/src/test/java/org/traccar/protocol/Gt06FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Gt06FrameDecoderTest.java
index a9d011277..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 {
diff --git a/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java
index 697908a4c..04d7fafb3 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;
@@ -18,6 +18,52 @@ public class Gt06ProtocolDecoderTest extends ProtocolTest {
"78780D01086471700328358100093F040D0A"));
verifyAttribute(decoder, binary(
+ "78782732180214123324ca0162bdf0041f45d900190b0a02d4000bc5270000ec025206040202005e07e10d0a"),
+ Position.KEY_ALARM, Position.ALARM_POWER_CUT);
+
+ verifyAttribute(decoder, binary(
+ "78782616170A080C0E24C0027C58AD0C2B8B0100454E0901CC0025030328E7A0005D4B13021EC373170D0A"),
+ Position.KEY_BATTERY_LEVEL, 93);
+
+ verifyAttribute(decoder, binary(
+ "787826161709130f3a2dcc02c55f2a089f9af9005c210901360481fe066d9b03413e420102035f92fe0d0a"),
+ Position.KEY_ALARM, Position.ALARM_SOS);
+
+ verifyAttribute(decoder, binary(
+ "78782f221709130f3a32cc02c55f2a089f9afc005c2101360481fe066d9b03000000000025d4000000000020002d036049d70d0a"),
+ Position.PREFIX_TEMP + 1, 0.32);
+
+ 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);
@@ -96,8 +142,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"));
@@ -110,7 +157,7 @@ public class Gt06ProtocolDecoderTest extends ProtocolTest {
verifyAttribute(decoder, binary(
"7979000E9B0332382E33A1E60D0A0289BE490D0A"),
- Position.PREFIX_TEMP + 1, 28.3);
+ Position.KEY_RESULT, "32382e33a1e60d0a");
verifyPosition(decoder, binary(
"7878353714080d05000ac500a886eb0b7522f000100001fe0a05ea004f1b000001002e0400002328003b0217c0003c0401020001002c468a0d0a"));
@@ -153,7 +200,7 @@ public class Gt06ProtocolDecoderTest extends ProtocolTest {
verifyAttribute(decoder, binary(
"797900149b03023539303042343843454238410300139ba40d0a"),
- Position.KEY_DRIVER_UNIQUE_ID, "5900B48CEB");
+ Position.KEY_RESULT, "0235393030423438434542384103");
verifyPosition(decoder, binary(
"787821121303120b2524c70138e363085b549003d43301940057d200cd52c000006aa1ca0d0a"));
@@ -186,7 +233,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 13463ea39..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;
diff --git a/src/test/java/org/traccar/protocol/Gt30ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gt30ProtocolDecoderTest.java
index 48c4306d6..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 {
diff --git a/src/test/java/org/traccar/protocol/H02FrameDecoderTest.java b/src/test/java/org/traccar/protocol/H02FrameDecoderTest.java
index c61c0f0c9..dc6eec84d 100644
--- a/src/test/java/org/traccar/protocol/H02FrameDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/H02FrameDecoderTest.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 H02FrameDecoderTest extends ProtocolTest {
diff --git a/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java
index e68f0814c..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;
diff --git a/src/test/java/org/traccar/protocol/H02ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/H02ProtocolEncoderTest.java
index 1be9421bb..2fc7af958 100644
--- a/src/test/java/org/traccar/protocol/H02ProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/H02ProtocolEncoderTest.java
@@ -1,7 +1,7 @@
package org.traccar.protocol;
-import org.junit.Before;
-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;
@@ -11,7 +11,7 @@ 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 {
@@ -19,7 +19,7 @@ public class H02ProtocolEncoderTest extends ProtocolTest {
private final Date time = Date.from(
LocalDateTime.of(LocalDate.now(), LocalTime.of(1, 2, 3)).atZone(ZoneOffset.systemDefault()).toInstant());
- @Before
+ @BeforeEach
public void before() throws Exception {
encoder = inject(new H02ProtocolEncoder(null));
}
diff --git a/src/test/java/org/traccar/protocol/HaicomProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HaicomProtocolDecoderTest.java
index 9d26d56c3..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 {
diff --git a/src/test/java/org/traccar/protocol/HomtecsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HomtecsProtocolDecoderTest.java
index 8fe4d2c8b..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 {
diff --git a/src/test/java/org/traccar/protocol/HoopoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HoopoProtocolDecoderTest.java
index ed7b0534b..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 {
diff --git a/src/test/java/org/traccar/protocol/HuaShengFrameDecoderTest.java b/src/test/java/org/traccar/protocol/HuaShengFrameDecoderTest.java
index 991e0b36d..26494f2b9 100644
--- a/src/test/java/org/traccar/protocol/HuaShengFrameDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/HuaShengFrameDecoderTest.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 HuaShengFrameDecoderTest extends ProtocolTest {
diff --git a/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java
index 7002d7e88..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;
@@ -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 6b902fb46..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 {
diff --git a/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java
index e91c84d79..164635109 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;
@@ -11,9 +11,58 @@ public class HuabaoProtocolDecoderTest extends ProtocolTest {
var decoder = inject(new HuabaoProtocolDecoder(null));
+ verifyAttribute(decoder, binary(
+ "7e55018c418560090010174701022106242122348476113550490700001c06000000074e0000000000000308100000100102020200000a030000000b0803363839363037650c0600000001000afa7e"),
+ "unlockResult", 0x65);
+
+ verifyPosition(decoder, binary(
+ "7e55018c378580120300032a06052117594022348474113550560705981e0400000002370ac30c0c28660308000000100101020200000a0301cc000c0600a2ffa7ff5e1b7e"));
+
+ verifyPosition(decoder, binary(
+ "7e55028c37850011000200c008052106305122348621113550170700001e080000000aaa0000000000000300000000100100020200140a030000000c06003bffa8ffc3c77e"));
+
+ verifyAttribute(decoder, binary(
+ "7E020000C2000000001862003F0000000400108042015A322206C869480017007B012E230918081550661D01CC1A0000099DC25727B0130000099DC26B27B00100000C40F89427B0711438393836303436393130323139303037383135336A0114503FF8AF05826BC8CA24E124F732B7C780C5484D6318C0A840412262D4BCEC26CA0234CCB85455D5114F12B650FA841FBEC0B03C67A21F501CAE6C384595028DA76B010060110065000012345678903930303137333833630B0012345678900E5C3080006804000003D46902016F6D7E"),
+ Position.KEY_DRIVER_UNIQUE_ID, "\u00909001738");
+
+ verifyAttribute(decoder, binary(
+ "7e02000072440061018577001b0000100000200066005ffb8b065dc8900000000000002309080513406316cd91fe1314da1080318122c3a19a6f6f3a0fc6318113660b01fe00000a069a011704de690201a16a011f6b010a6c0f35313031303139313539323738373571143839363231303030313931353932373837353546407e"),
+ "lock2Battery", 4.038);
+
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"));
diff --git a/src/test/java/org/traccar/protocol/HuabaoProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/HuabaoProtocolEncoderTest.java
index 6f9c8250a..87cc2b12e 100644
--- a/src/test/java/org/traccar/protocol/HuabaoProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/HuabaoProtocolEncoderTest.java
@@ -1,13 +1,13 @@
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 {
diff --git a/src/test/java/org/traccar/protocol/HunterProProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HunterProProtocolDecoderTest.java
index 5503ab02c..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 {
diff --git a/src/test/java/org/traccar/protocol/IdplProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/IdplProtocolDecoderTest.java
index 1bd52ce89..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 {
diff --git a/src/test/java/org/traccar/protocol/IntellitracProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/IntellitracProtocolDecoderTest.java
index b6e12aef5..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 {
diff --git a/src/test/java/org/traccar/protocol/IotmProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/IotmProtocolDecoderTest.java
index c668084a1..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 {
diff --git a/src/test/java/org/traccar/protocol/ItsFrameDecoderTest.java b/src/test/java/org/traccar/protocol/ItsFrameDecoderTest.java
index 26f28916c..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 {
diff --git a/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java
index dd8d3a0d9..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;
diff --git a/src/test/java/org/traccar/protocol/ItsProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/ItsProtocolEncoderTest.java
index 195fc4258..b9bda0c0a 100644
--- a/src/test/java/org/traccar/protocol/ItsProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/ItsProtocolEncoderTest.java
@@ -1,10 +1,10 @@
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 {
diff --git a/src/test/java/org/traccar/protocol/Ivt401ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Ivt401ProtocolDecoderTest.java
index b08dff4e7..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 {
diff --git a/src/test/java/org/traccar/protocol/JidoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/JidoProtocolDecoderTest.java
index b275e6d01..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 {
diff --git a/src/test/java/org/traccar/protocol/JpKorjarProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/JpKorjarProtocolDecoderTest.java
index a1b0acac5..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 {
diff --git a/src/test/java/org/traccar/protocol/JsonFrameDecoderTest.java b/src/test/java/org/traccar/protocol/JsonFrameDecoderTest.java
index 1bc5d8480..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 {
diff --git a/src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java
index 8e408e50f..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 {
@@ -11,6 +11,14 @@ public class Jt600FrameDecoderTest extends ProtocolTest {
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"),
decoder.decode(null, null, binary("2480413009781914003406102107544354193631006213423b00000000006c070000000020e064f91ea0671d00020f0f0f0f0f0f0f0f0f0f07f100ea0f6e")));
diff --git a/src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java
index fc52e32e3..32213fda1 100644
--- a/src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.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 Jt600ProtocolDecoderTest extends ProtocolTest {
diff --git a/src/test/java/org/traccar/protocol/Jt600ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Jt600ProtocolEncoderTest.java
index fdd73b9ce..5c8c260b7 100644
--- a/src/test/java/org/traccar/protocol/Jt600ProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/Jt600ProtocolEncoderTest.java
@@ -1,10 +1,10 @@
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 Jt600ProtocolEncoderTest extends ProtocolTest {
Jt600ProtocolEncoder encoder = new Jt600ProtocolEncoder(null);
diff --git a/src/test/java/org/traccar/protocol/KenjiProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/KenjiProtocolDecoderTest.java
index a705d8082..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 {
diff --git a/src/test/java/org/traccar/protocol/KhdProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/KhdProtocolDecoderTest.java
index 155493bea..3097c02e8 100644
--- a/src/test/java/org/traccar/protocol/KhdProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/KhdProtocolDecoderTest.java
@@ -1,7 +1,8 @@
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 {
@@ -10,6 +11,14 @@ public class KhdProtocolDecoderTest extends ProtocolTest {
var decoder = inject(new KhdProtocolDecoder(null));
+ verifyAttribute(decoder, binary(
+ "2929A300403099934C2004030943310000000000000000000000007B0000007FFF0E0000E70014000000000018050B01303030314330334437312102007B2203140DDA610D"),
+ Position.KEY_DRIVER_UNIQUE_ID, "0001C03D71");
+
+ 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 468083f35..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;
diff --git a/src/test/java/org/traccar/protocol/L100FrameDecoderTest.java b/src/test/java/org/traccar/protocol/L100FrameDecoderTest.java
index 084ae0498..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 {
diff --git a/src/test/java/org/traccar/protocol/L100ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/L100ProtocolDecoderTest.java
index 56281cda0..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 {
diff --git a/src/test/java/org/traccar/protocol/LacakProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/LacakProtocolDecoderTest.java
index 45544241f..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 {
diff --git a/src/test/java/org/traccar/protocol/LaipacProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/LaipacProtocolDecoderTest.java
index aacf9abc8..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 {
diff --git a/src/test/java/org/traccar/protocol/LeafSpyProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/LeafSpyProtocolDecoderTest.java
index ea31bc99d..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 {
diff --git a/src/test/java/org/traccar/protocol/M2cProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/M2cProtocolDecoderTest.java
index 674738c82..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 {
diff --git a/src/test/java/org/traccar/protocol/M2mProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/M2mProtocolDecoderTest.java
index 0d812ebfc..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 {
diff --git a/src/test/java/org/traccar/protocol/MaestroProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MaestroProtocolDecoderTest.java
index be0fe502e..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 {
diff --git a/src/test/java/org/traccar/protocol/ManPowerProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ManPowerProtocolDecoderTest.java
index a77043b9d..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 {
diff --git a/src/test/java/org/traccar/protocol/Mavlink2ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Mavlink2ProtocolDecoderTest.java
index 0c74d4772..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 {
diff --git a/src/test/java/org/traccar/protocol/MegastekFrameDecoderTest.java b/src/test/java/org/traccar/protocol/MegastekFrameDecoderTest.java
index 854564cd2..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 {
diff --git a/src/test/java/org/traccar/protocol/MegastekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MegastekProtocolDecoderTest.java
index 964c59927..227fb20e0 100644
--- a/src/test/java/org/traccar/protocol/MegastekProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/MegastekProtocolDecoderTest.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;
diff --git a/src/test/java/org/traccar/protocol/MeiligaoFrameDecoderTest.java b/src/test/java/org/traccar/protocol/MeiligaoFrameDecoderTest.java
index 379cf28f9..2647c49db 100644
--- a/src/test/java/org/traccar/protocol/MeiligaoFrameDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/MeiligaoFrameDecoderTest.java
@@ -1,10 +1,10 @@
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 {
diff --git a/src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java
index 4c6eae847..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;
diff --git a/src/test/java/org/traccar/protocol/MeiligaoProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/MeiligaoProtocolEncoderTest.java
index 1f2e5f7e3..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;
diff --git a/src/test/java/org/traccar/protocol/MeitrackFrameDecoderTest.java b/src/test/java/org/traccar/protocol/MeitrackFrameDecoderTest.java
index a55935ed2..f86b676a6 100644
--- a/src/test/java/org/traccar/protocol/MeitrackFrameDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/MeitrackFrameDecoderTest.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 MeitrackFrameDecoderTest extends ProtocolTest {
diff --git a/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java
index ce0a1e922..74a180b11 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;
@@ -11,6 +11,36 @@ public class MeitrackProtocolDecoderTest extends ProtocolTest {
var decoder = inject(new MeitrackProtocolDecoder(null));
+ verifyAttribute(decoder, binary(
+ "24246D3230312C3836393430393036323730323834332C4343452C000000000100A7002A000D05010626070914001502930194009500960097629D209E63A1640E0824000956000A07000B2A001606001704001901001A0E0B91280092280099EF049C52009F1B004023000C0215B9F2FF035855F506041E0F142D0C01708C010D748AC2001C012000009A000000009BD0623E02A0889FF201A2D61A0000A542020000FEF4A3D50900030E0CFE010A007F466FFC0000000049090401000000000000004B0501010232472A44360D0A"),
+ Position.KEY_HOURS, 644515 * 60000L);
+
+ verifyAttribute(decoder, binary(
+ "2424593136312c3836323039303035303031363139332c4343452c0000000001007f0017000705010607071714001500fe69601b00070800000971000a13000b19001605001acc0440230006029779570103eb5bcc06041ff0e8290c430100000d780400001c01000000030e0ccc010000922781abb90ca4fffe731e0109746e6873656e736f72ac233f6e219064051b753b00000000000000004b060101034c54452a42380d0a"),
+ "tagName", "tnhsensor");
+
+ verifyAttribute(decoder, binary(
+ "2424683136342C3836363334343035333039353238322C4343452C000000000100820018000505000600070B14001500080800000900000A00000B00001606001A0000402300FE90000006022E79570103E55CCC0604E1FDB32B0CC32C00000D58EB02001C01000000050E0CCC010000B627BF11000000004B1001010D475052532847534D2039303029FEA50601FFFFFF7FFFFEA80701010258023800FEB20501010000002A41360D0A"),
+ "battery2Level", 88);
+
+ verifyPositions(decoder, binary(
+ "2424533232312c3836323331313036323737393431362c4343452c000000000100bb001c0006012305000600071f1500fe6961050800000900000a00000b00001aca000702a72c52030340a0c90004f3408b2c0ca80100000d238d08001c01000000fe37000000000a0e0cf00001002700167aa601a0ff1d082abd890a491cd2ff1e0828bd890a491cd2ff1f0842490f526db0caff20083c286d5b082cc9ff21083e286d5b082cc9ff2208ac233fc0d2e0c7ff2308b0411d64d9d5c7ff2408ae233fc0d496c3ff4b150101124c54452845555452414e2d42414e443230292a38420d0a"));
+
+ 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"));
diff --git a/src/test/java/org/traccar/protocol/MeitrackProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/MeitrackProtocolEncoderTest.java
index cc8847db2..ac9854b8e 100644
--- a/src/test/java/org/traccar/protocol/MeitrackProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/MeitrackProtocolEncoderTest.java
@@ -1,10 +1,10 @@
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 {
diff --git a/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java
index 5e36abe5b..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;
diff --git a/src/test/java/org/traccar/protocol/MilesmateProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MilesmateProtocolDecoderTest.java
index 275672554..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 {
diff --git a/src/test/java/org/traccar/protocol/MiniFinderProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MiniFinderProtocolDecoderTest.java
index a8f1be855..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;
@@ -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 502f8e8bf..f61779a38 100644
--- a/src/test/java/org/traccar/protocol/MiniFinderProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/MiniFinderProtocolEncoderTest.java
@@ -1,10 +1,10 @@
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 {
diff --git a/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java
index 0809996f6..126334767 100644
--- a/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java
@@ -1,7 +1,8 @@
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 {
@@ -10,6 +11,20 @@ public class Minifinder2ProtocolDecoderTest extends ProtocolTest {
var decoder = inject(new Minifinder2ProtocolDecoder(null));
+ verifyPositions(decoder, false, binary(
+ "ab105a0512e19404011001383632333131303632373037333735093743c3ec640000000009374dc3ec6400000000093750c3ec6400000080092455c3ec640203935e0f22a318d6c7baacd6a2546751467bd009246ac3ec640203b35e0f22a318d6c7baacd6a2546751467bd009246cc3ec640203b35e0f22a318d6c7baacd6a2546751467bd009247ec3ec640203b35e0f22a318d6c7baacd6a2546751467bd0092492c3ec640203b35e0f22a318d6c7baacd6a2546751467bd00924a6c3ec640203b35e0f22a318d6c7baacd6a2546751467bd00924bac3ec640203b35e0f22a318d6c7baacd6a2546751467bd00924d2c3ec640203b35e0f22a7083a2f201a83a3f8084f84ae560924e7c3ec640203b35e0f22a7083a2f201a83a3f8084f84ae560924fbc3ec640203b35e0f22a7083a2f201a83a3f8084f84ae5609240fc4ec640203b35e0f22a7083a2f201a83a3f8084f84ae56092423c4ec640203b35d0f22a7083a2f201a83a3f8084f84ae56092437c4ec640203cb5d0f22a7083a2f201a83a3f8084f84ae5609244fc4ec640003cb5d092464c4ec640003cb5d092478c4ec640003cb5d09248cc4ec640003cb5d0924a0c4ec640003cb5d0924b4c4ec640003cb5d0924ccc4ec640003cb5d0924e5c4ec640003cb5d0924fec4ec6400037b5d092413c5ec6400037b5d092427c5ec6400017b5d0924b785ed640003cb530924d085ed640003ab530924e985ed640003ab530924fe85ed640003ab5309241286ed640003ab5309242686ed640003ab5309243a86ed640003ab5309244e86ed640003ab5309246786ed640003ab5309248086ed640003ab5309249986ed6400037b530924b286ed6400037b530924c686ed6400037b530924da86ed6400037b530924ee86ed6400037b5309240287ed6400037b5309241687ed6400037b5309242f87ed6400037b5309244787ed640003835309246187ed640003835309247a87ed640003835309249287ed64000383530924ab87ed64000383530924c487ed64000383530924d987ed64000383530924ed87ed640003835309240188ed640003835309241588ed640003835309242988ed640003d35309243a88ed640003d3530d02000000803788ed640000000009374188ed640400000009244188ed640003d35309244288ed640003d35309374b88ed640500000009244b88ed640003d35309375588ed640500000009245588ed640003d35309245788ed640003d35309375f88ed640700000009245f88ed640003d35309376988ed640800000009246988ed640003d35309246b88ed640203d3530f22a502184a2cfba0a42c768af4ab5009247188ed640203d3530f22a502184a2cfba0a42c768af4ab5009377388ed640a00000009247688ed640203d3530f22a502184a2cfba0a42c768af4ab5009247b88ed640203d3530f22a502184a2cfba0a42c768af4ab5009377d88ed640300000009247e88ed640203d3530f22a502184a2cfba0a42c768af4ab5009248088ed640203d3530f22a502184a2cfba0a42c768af4ab5009248588ed640203d3530f22a502184a2cfba0a42c768af4ab5009378788ed640000000009248a88ed640203d3530f22a502184a2cfba0a42c768af4ab5009248f88ed640203d3530f22a502184a2cfba0a42c768af4ab5009379188ed640000000009379288ed640000008009249288ed640203d3530f22a502184a2cfba0a42c768af4ab5009249488ed640203d3530f22a502184a2cfba0a42c768af4ab5009249988ed640203d3530f22a502184a2cfba0a42c768af4ab5009249e88ed640203d3530f22a502184a2cfba0a42c768af4ab500924a388ed640203d3530f22a502184a2cfba0a42c768af4ab500924a688ed640203d3530f22a502184a2cfba0a42c768af4ab50"));
+
+ verifyAttributes(decoder, binary(
+ "ab00cc029c9b0000020501040518200502cf290001100338363233313130363534393538393515043839343632303338303735303031383830343034070539eed3f9cec705064f93a7650507010b00002908cf2900010020050000d0000068915b00ae0000004f637420313920323032330031303a35393a3332261b53494d37353030457c4231315630325f3231303330337c50312e30325f3230323231313034050900000000050a00000003070b000000001e01070b010000001e01070b020000001e01070b030000001e01060c0000000000050d6e600580020e04050f0708008002100002110f02126406134d46303758041404a20e08160000000000000008160100000000000008160200000000000008160300000000000008160400000000000008160500000000000008160600000000000008160700000000000008160800000000000008160900000000000020177777772e676f6f676c652e636f6d2f6d6170733f713d252e37662c252e3766211868747470733a2f2f6c6f632e6d696e6966696e6465722e636f6d2f25732f25730519000001fe101a4d4630372e343631302e3233303300021c640a1d802acee6212966cf0803207b3e03217b040230000230010230020230030230040230050230060230070230080230090231000532580214010533820000000e406d326d2e74656c65322e636f6d014101421c4380431468756e7465726465762e6d696e6966696e6465722e636f6d0d440e01008005000000100e0000054505002c01014705500f1400f00d511002e803b84d7f0d0246f6430d511100f40100000000000000000d511200f40100000000000000000d511300f401000000000000000005527800030005532c0100000354500005551e002d400256050b57201c00002c010000ed61025d01055c890a0000057000000000037100001472000000000000000000000000000000000000000568f0000100057500000000074de36000000000"));
+
+ verifyAttribute(decoder, binary(
+ "ab101c00d6f61e000110013836333932313033393939363038300937efd201640c000000"),
+ "barkCount", 12L);
+
+ verifyAttribute(decoder, binary(
+ "ab00030008c700007f0100"),
+ Position.KEY_RESULT, "0");
+
verifyAttribute(decoder, binary(
"ab102600080f1400011001383633393231303339393833343736092429b347633003a96409020000008027b34763"),
"bark", true);
@@ -20,10 +35,10 @@ public class Minifinder2ProtocolDecoderTest extends ProtocolTest {
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..32c8a9ce6
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Minifinder2ProtocolEncoderTest.java
@@ -0,0 +1,28 @@
+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));
+
+ encoder.setModelOverride("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 fea74db7a..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;
diff --git a/src/test/java/org/traccar/protocol/MoovboxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MoovboxProtocolDecoderTest.java
index 82781550e..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 {
diff --git a/src/test/java/org/traccar/protocol/MotorProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MotorProtocolDecoderTest.java
index f3ebb8e5c..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 {
diff --git a/src/test/java/org/traccar/protocol/MtxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MtxProtocolDecoderTest.java
index 8a5e228c7..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 {
diff --git a/src/test/java/org/traccar/protocol/MxtProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MxtProtocolDecoderTest.java
index 68a68c9e8..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 {
diff --git a/src/test/java/org/traccar/protocol/NavigilProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NavigilProtocolDecoderTest.java
index 8eda687cc..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 {
diff --git a/src/test/java/org/traccar/protocol/NavisFrameDecoderTest.java b/src/test/java/org/traccar/protocol/NavisFrameDecoderTest.java
index 29327f7e1..8678a55ba 100644
--- a/src/test/java/org/traccar/protocol/NavisFrameDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/NavisFrameDecoderTest.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 NavisFrameDecoderTest extends ProtocolTest {
diff --git a/src/test/java/org/traccar/protocol/NavisProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NavisProtocolDecoderTest.java
index 960ed1442..56f5a4c9f 100644
--- a/src/test/java/org/traccar/protocol/NavisProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/NavisProtocolDecoderTest.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 NavisProtocolDecoderTest extends ProtocolTest {
diff --git a/src/test/java/org/traccar/protocol/NavisetFrameDecoderTest.java b/src/test/java/org/traccar/protocol/NavisetFrameDecoderTest.java
index d15d01cc0..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 {
diff --git a/src/test/java/org/traccar/protocol/NavisetProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NavisetProtocolDecoderTest.java
index d7643b50c..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 {
diff --git a/src/test/java/org/traccar/protocol/NavtelecomFrameDecoderTest.java b/src/test/java/org/traccar/protocol/NavtelecomFrameDecoderTest.java
index 360f92447..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 {
@@ -24,7 +24,7 @@ public class NavtelecomFrameDecoderTest extends ProtocolTest {
}
- @Ignore
+ @Disabled
@Test
public void testDecodeFull() throws Exception {
diff --git a/src/test/java/org/traccar/protocol/NavtelecomProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NavtelecomProtocolDecoderTest.java
index 301a72b2a..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 {
diff --git a/src/test/java/org/traccar/protocol/NdtpV6ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NdtpV6ProtocolDecoderTest.java
index c63c19a29..67e88cb5a 100644
--- a/src/test/java/org/traccar/protocol/NdtpV6ProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/NdtpV6ProtocolDecoderTest.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 NdtpV6ProtocolDecoderTest extends ProtocolTest {
diff --git a/src/test/java/org/traccar/protocol/NeosProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NeosProtocolDecoderTest.java
index 4e9e55f62..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 {
diff --git a/src/test/java/org/traccar/protocol/NetProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NetProtocolDecoderTest.java
index 239d892f8..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 {
diff --git a/src/test/java/org/traccar/protocol/NiotProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NiotProtocolDecoderTest.java
index 03aaa49aa..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 {
diff --git a/src/test/java/org/traccar/protocol/NoranProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NoranProtocolDecoderTest.java
index 3f1ec7aee..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 {
diff --git a/src/test/java/org/traccar/protocol/NoranProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/NoranProtocolEncoderTest.java
index d1b28525c..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;
diff --git a/src/test/java/org/traccar/protocol/NtoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NtoProtocolDecoderTest.java
new file mode 100644
index 000000000..424eaadde
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/NtoProtocolDecoderTest.java
@@ -0,0 +1,19 @@
+package org.traccar.protocol;
+
+import org.junit.jupiter.api.Test;
+import org.traccar.ProtocolTest;
+
+public class NtoProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ var decoder = inject(new NtoProtocolDecoder(null));
+
+ verifyPosition(decoder, text(
+ "^NB,880002023090601,N00,050923,233519,V,N,2236.1994,E,11315.4645,5,0,000000000000,460:00:0:75217090,-04:00,,1693971319,31,2DA5"),
+ position("2023-09-05 23:35:19.000", false, 22.60332, 113.25774));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/NvsFrameDecoderTest.java b/src/test/java/org/traccar/protocol/NvsFrameDecoderTest.java
index dd5e1d9b9..d4dcdcc60 100644
--- a/src/test/java/org/traccar/protocol/NvsFrameDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/NvsFrameDecoderTest.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 NvsFrameDecoderTest extends ProtocolTest {
diff --git a/src/test/java/org/traccar/protocol/NvsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NvsProtocolDecoderTest.java
index 61d050679..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 {
diff --git a/src/test/java/org/traccar/protocol/NyitechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NyitechProtocolDecoderTest.java
index b3bd9aca7..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 {
diff --git a/src/test/java/org/traccar/protocol/ObdDongleProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ObdDongleProtocolDecoderTest.java
index 8272fe41e..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 {
diff --git a/src/test/java/org/traccar/protocol/OigoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OigoProtocolDecoderTest.java
index 6015f1d18..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 {
diff --git a/src/test/java/org/traccar/protocol/OkoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OkoProtocolDecoderTest.java
index 19c96ed9a..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 {
diff --git a/src/test/java/org/traccar/protocol/OmnicommFrameDecoderTest.java b/src/test/java/org/traccar/protocol/OmnicommFrameDecoderTest.java
index 8e8d9b1cf..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 {
diff --git a/src/test/java/org/traccar/protocol/OmnicommProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OmnicommProtocolDecoderTest.java
index 5b3b08194..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 {
diff --git a/src/test/java/org/traccar/protocol/OpenGtsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OpenGtsProtocolDecoderTest.java
index 5494301d8..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 {
diff --git a/src/test/java/org/traccar/protocol/OrbcommProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OrbcommProtocolDecoderTest.java
index 408053496..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 {
diff --git a/src/test/java/org/traccar/protocol/OrionProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OrionProtocolDecoderTest.java
index f5b98574c..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 {
diff --git a/src/test/java/org/traccar/protocol/OsmAndProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OsmAndProtocolDecoderTest.java
index 3b8a94613..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 {
diff --git a/src/test/java/org/traccar/protocol/OutsafeProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OutsafeProtocolDecoderTest.java
index edb7d1aad..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 {
diff --git a/src/test/java/org/traccar/protocol/OwnTracksProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OwnTracksProtocolDecoderTest.java
index 03332e7fe..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 {
diff --git a/src/test/java/org/traccar/protocol/PacificTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PacificTrackProtocolDecoderTest.java
index bde464162..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 {
diff --git a/src/test/java/org/traccar/protocol/PathAwayProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PathAwayProtocolDecoderTest.java
index 97020343f..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 {
diff --git a/src/test/java/org/traccar/protocol/PiligrimProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PiligrimProtocolDecoderTest.java
index 0dd00462d..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 {
diff --git a/src/test/java/org/traccar/protocol/PluginProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PluginProtocolDecoderTest.java
index 8b15d70a6..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;
diff --git a/src/test/java/org/traccar/protocol/PolteProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PolteProtocolDecoderTest.java
index 8bf109d11..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 {
diff --git a/src/test/java/org/traccar/protocol/PortmanProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PortmanProtocolDecoderTest.java
index 8bc16d373..b86d7340e 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 {
@@ -11,6 +11,9 @@ public class PortmanProtocolDecoderTest extends ProtocolTest {
var decoder = inject(new PortmanProtocolDecoder(null));
verifyPosition(decoder, text(
+ "%%863922034547720,A,231119031316,N3640.4542E11707.5992,000,000,NA,95000000,NA,254,24,1.00,24"));
+
+ verifyPosition(decoder, text(
"$EXT,P0RTMANGRANT,A,210609201710,N0951.6879W08357.0129,0,0,NA,NA,11,25,174700.25,NA,01820000,108"));
verifyPosition(decoder, text(
diff --git a/src/test/java/org/traccar/protocol/PortmanProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/PortmanProtocolEncoderTest.java
index b4c334a0c..41e78cf6c 100644
--- a/src/test/java/org/traccar/protocol/PortmanProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/PortmanProtocolEncoderTest.java
@@ -1,10 +1,10 @@
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 {
diff --git a/src/test/java/org/traccar/protocol/PositrexProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PositrexProtocolDecoderTest.java
new file mode 100644
index 000000000..bf4f9c765
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/PositrexProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.jupiter.api.Test;
+import org.traccar.ProtocolTest;
+
+public class PositrexProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ var decoder = inject(new PositrexProtocolDecoder(null));
+
+ verifyPosition(decoder, binary(
+ "8280902e002b81c99fd607033905008b1c000003ae00003c9c000054ee00000079000000000d34d43f0fffffffda0000000000104fb80000204086464717807f8931082622128190980fffff862261047296590fffff"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/PretraceProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PretraceProtocolDecoderTest.java
index 5dbde7846..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 {
diff --git a/src/test/java/org/traccar/protocol/PretraceProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/PretraceProtocolEncoderTest.java
index d3218d4a8..da18441f5 100644
--- a/src/test/java/org/traccar/protocol/PretraceProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/PretraceProtocolEncoderTest.java
@@ -1,10 +1,10 @@
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 {
diff --git a/src/test/java/org/traccar/protocol/PricolProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PricolProtocolDecoderTest.java
index 8c2081641..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 {
diff --git a/src/test/java/org/traccar/protocol/ProgressProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ProgressProtocolDecoderTest.java
index 9129a3079..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 {
diff --git a/src/test/java/org/traccar/protocol/PstFrameDecoderTest.java b/src/test/java/org/traccar/protocol/PstFrameDecoderTest.java
index 172d85df6..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 {
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 880caf727..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 {
diff --git a/src/test/java/org/traccar/protocol/PstProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/PstProtocolEncoderTest.java
index 6c3ff71b6..b6530d815 100644
--- a/src/test/java/org/traccar/protocol/PstProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/PstProtocolEncoderTest.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;
diff --git a/src/test/java/org/traccar/protocol/Pt215ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Pt215ProtocolDecoderTest.java
index a5f5d7e77..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 {
diff --git a/src/test/java/org/traccar/protocol/Pt3000ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Pt3000ProtocolDecoderTest.java
index f7b278139..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 {
diff --git a/src/test/java/org/traccar/protocol/Pt502FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Pt502FrameDecoderTest.java
index 854c789b8..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 {
diff --git a/src/test/java/org/traccar/protocol/Pt502ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Pt502ProtocolDecoderTest.java
index f310b2227..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;
diff --git a/src/test/java/org/traccar/protocol/Pt502ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Pt502ProtocolEncoderTest.java
index c97093e26..98fe147b2 100644
--- a/src/test/java/org/traccar/protocol/Pt502ProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/Pt502ProtocolEncoderTest.java
@@ -1,10 +1,10 @@
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 {
diff --git a/src/test/java/org/traccar/protocol/Pt60ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Pt60ProtocolDecoderTest.java
index b198ac28e..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 {
diff --git a/src/test/java/org/traccar/protocol/PuiProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PuiProtocolDecoderTest.java
new file mode 100644
index 000000000..1bbf17361
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/PuiProtocolDecoderTest.java
@@ -0,0 +1,23 @@
+package org.traccar.protocol;
+
+import io.netty.handler.codec.mqtt.MqttMessageBuilders;
+import io.netty.handler.codec.mqtt.MqttQoS;
+import org.junit.jupiter.api.Test;
+import org.traccar.ProtocolTest;
+
+public class PuiProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ var decoder = inject(new PuiProtocolDecoder(null));
+
+ verifyNull(decoder, MqttMessageBuilders.connect().clientId(
+ "123456789012345").build());
+
+ verifyPosition(decoder, MqttMessageBuilders.publish().payload(buffer(
+ "{ \"id\": \"015262001044848\", \"ts\": \"2023-06-01T03:09:51.362Z\", \"rpt\": \"hf\", \"location\": { \"lat\": 33.91233, \"lon\": -84.20784 }, \"bear\": 70, \"spd\": 2482, \"ign\": \"on\" }")).qos(MqttQoS.EXACTLY_ONCE).messageId(1).build());
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/R12wProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/R12wProtocolDecoderTest.java
index a363022f0..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 {
diff --git a/src/test/java/org/traccar/protocol/RaceDynamicsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RaceDynamicsProtocolDecoderTest.java
index ff40c19a3..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 {
diff --git a/src/test/java/org/traccar/protocol/RadarProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RadarProtocolDecoderTest.java
index b5a2555b1..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 {
diff --git a/src/test/java/org/traccar/protocol/RamacProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RamacProtocolDecoderTest.java
new file mode 100644
index 000000000..86734a259
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/RamacProtocolDecoderTest.java
@@ -0,0 +1,26 @@
+package org.traccar.protocol;
+
+import io.netty.handler.codec.http.HttpMethod;
+import org.junit.jupiter.api.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class RamacProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ var decoder = inject(new RamacProtocolDecoder(null));
+
+ verifyAttributes(decoder, request(HttpMethod.POST, "/",
+ buffer("{\"PacketType\": 0,\"SeqNumber\": 4,\"UpdateDate\": \"2022-05-06 12:25:35\",\"Alert\": 42,\"AlertMessage\": \"Low Battery\",\"Mode\": 1,\"ModeText\": \"Help Me\",\"SigfoxTXInterval\": 2,\"GpsFixInterval\": 3,\"SigfoxTXIntervalText\": \"2 Seconds\",\"GpsFixIntervalText\": \"3 Seconds\",\"BatteryPercentage\": 4,\"Battery\": 0.1,\"Temperature\": -22,\"HwVersion\": 7,\"FirmwareVersion\": 8,\"DeviceId\": \"A10001\",\"DeviceType\": \"12\",\"DeviceTypeText\": \"RAMAC P1\"}")));
+
+ verifyPosition(decoder, request(HttpMethod.POST, "/",
+ buffer("{\"PacketType\": 1,\"SeqNumber\": 4,\"UpdateDate\": \"2022-05-06 12:25:35\",\"Alert\": 0,\"AlertMessage\": \"\",\"Latitude\": -25.87586735939189,\"Longitude\": 28.179579268668846,\"Speed\": 1,\"COG\": 3,\"EstimatedAccuracy\": 3,\"LastLocation\": 0,\"LastLocationText\": \"NEW LOCATION\",\"IsMoving\": 0,\"IsMovingText\": \"STATIONARY\",\"GpsEvent\": 5,\"GpsEventText\": \"Heartbeat\",\"DeviceId\": \"A10001\",\"DeviceType\": \"12\",\"DeviceTypeText\": \"RAMAC P1\"}")));
+
+ verifyPosition(decoder, request(HttpMethod.POST, "/",
+ buffer("{\"PacketType\": 2,\"SeqNumber\": 4,\"UpdateDate\": \"2022-05-06 12:25:35\",\"Alert\": 19,\"AlertMessage\": \"P1 Panic\",\"Event\": 16,\"DeviceId\": \"A10001\",\"DeviceType\": \"12\",\"DeviceTypeText\": \"RAMAC P1\",\"Latitude\": -25.875867359392,\"Longitude\": 28.179579268669,\"LocationDateTime\": \"2022-05-05 08:48:11\"}")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/RaveonProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RaveonProtocolDecoderTest.java
index 3da671dbf..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 {
diff --git a/src/test/java/org/traccar/protocol/RecodaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RecodaProtocolDecoderTest.java
index 5bdfd6816..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 {
diff --git a/src/test/java/org/traccar/protocol/RetranslatorProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RetranslatorProtocolDecoderTest.java
index eb591a5f6..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 {
diff --git a/src/test/java/org/traccar/protocol/RfTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RfTrackProtocolDecoderTest.java
index df19f01c6..8073c5459 100644
--- a/src/test/java/org/traccar/protocol/RfTrackProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/RfTrackProtocolDecoderTest.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 RfTrackProtocolDecoderTest extends ProtocolTest {
diff --git a/src/test/java/org/traccar/protocol/RitiProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RitiProtocolDecoderTest.java
index 0d7eeb0df..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 {
diff --git a/src/test/java/org/traccar/protocol/RoboTrackFrameDecoderTest.java b/src/test/java/org/traccar/protocol/RoboTrackFrameDecoderTest.java
index e4b30538c..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 {
diff --git a/src/test/java/org/traccar/protocol/RoboTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RoboTrackProtocolDecoderTest.java
index db1617c9e..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 {
diff --git a/src/test/java/org/traccar/protocol/RstProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RstProtocolDecoderTest.java
index b301507fb..a750d0311 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;
@@ -11,9 +11,21 @@ public class RstProtocolDecoderTest extends ProtocolTest {
var decoder = inject(new RstProtocolDecoder(null));
+ verifyAttribute(decoder, text(
+ "RST;A;RST-MINI-4Gv3;V9.10;009521405;1;134;FIM;"),
+ Position.KEY_RESULT, "134");
+
+ verifyAttribute(decoder, text(
+ "RST;A;RST-MINIv5;V9.08;009767055;248;55;14-12-2023 19:34:20;14-12-2023 19:34:21;-12.923640;-38.388313;0;14;17;1;4;15;00;B0;00;1A;02;12.18;4.02;65;21;FE;0000;01;C0;001606017031;0002;FIM;"),
+ Position.KEY_DRIVER_UNIQUE_ID, "001606017031");
+
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;"));
+ verifyAttribute(decoder, text(
+ "RST;A;RST-MINI-4Gv3;V9.10;009521405;13;1;21-11-2023 20:04:18;21-11-2023 20:04:18;-12.923627;-38.388287;1;165;29;1;5;2;00;B0;00;1A;02;11.89;3.90;73;31;FE;0000;01;40;00800061;0;184;2;4;4;6;434.0000;2;0;-49;1815;37391;724;255;263;00000000;FIM;"),
+ Position.KEY_IGNITION, true);
+
verifyPosition(decoder, text(
"RST;L;RST-MINIv2;V7.02;008068078;61;1;27-01-2020 21:36:33;27-01-2020 21:36:33;-16.696159;-49.284275;0;67;786;1;15;0;00;B0;00;19;06;12.42;4.16;79;20;FE;0000;01;E0;00800020;0;467;FIM;"));
diff --git a/src/test/java/org/traccar/protocol/RuptelaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RuptelaProtocolDecoderTest.java
index 89d4a02cc..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 {
@@ -13,6 +13,9 @@ public class RuptelaProtocolDecoderTest extends ProtocolTest {
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 0c4fc6767..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;
diff --git a/src/test/java/org/traccar/protocol/S168ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/S168ProtocolDecoderTest.java
index 2b7f40c82..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 {
diff --git a/src/test/java/org/traccar/protocol/SabertekFrameDecoderTest.java b/src/test/java/org/traccar/protocol/SabertekFrameDecoderTest.java
index 15b1d0451..689de29b2 100644
--- a/src/test/java/org/traccar/protocol/SabertekFrameDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/SabertekFrameDecoderTest.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 SabertekFrameDecoderTest extends ProtocolTest {
diff --git a/src/test/java/org/traccar/protocol/SabertekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SabertekProtocolDecoderTest.java
index 6aafa325f..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 {
diff --git a/src/test/java/org/traccar/protocol/SanavProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SanavProtocolDecoderTest.java
index d0ae6fabf..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 {
diff --git a/src/test/java/org/traccar/protocol/SanulProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SanulProtocolDecoderTest.java
index 57398dd84..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 {
diff --git a/src/test/java/org/traccar/protocol/SatsolProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SatsolProtocolDecoderTest.java
index 0fe16377d..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 {
diff --git a/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java
index 66d5f5e69..c7d0671c0 100644
--- a/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java
@@ -1,10 +1,13 @@
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.Device;
import org.traccar.model.Position;
+import static org.mockito.Mockito.when;
+
public class SigfoxProtocolDecoderTest extends ProtocolTest {
@Test
@@ -43,6 +46,11 @@ public class SigfoxProtocolDecoderTest extends ProtocolTest {
verifyPosition(decoder, request(HttpMethod.POST, "/",
buffer("%7B++%22device%22%3A%222BF839%22%2C++%22time%22%3A1510605882%2C++%22duplicate%22%3Afalse%2C++%22snr%22%3A45.61%2C++%22station%22%3A%2235A9%22%2C++%22data%22%3A%2200bd6475e907398e562d01b9%22%2C++%22avgSnr%22%3A45.16%2C++%22lat%22%3A-38.0%2C++%22lng%22%3A145.0%2C++%22rssi%22%3A-98.00%2C++%22seqNumber%22%3A228+%7D=")));
+ decoder.setModelOverride("Amber");
+
+ verifyPosition(decoder, request(HttpMethod.POST, "/",
+ buffer("{ \"deviceId\":\"284019F\", \"timestamp\":\"1707375610\", \"seqNo\":\"42\", \"data\":\"0100b019ffe8645d0019e513\", \"linkQuality\":\"Excellent\", \"operator\":\"SIGFOX_South_Africa_Sqwidnet\", \"country\":\"710\" }")));
+
}
}
diff --git a/src/test/java/org/traccar/protocol/SiwiProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SiwiProtocolDecoderTest.java
index 0d7eb4f16..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 {
diff --git a/src/test/java/org/traccar/protocol/SkypatrolProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SkypatrolProtocolDecoderTest.java
index e59e3c3f0..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 {
diff --git a/src/test/java/org/traccar/protocol/SmartSoleProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SmartSoleProtocolDecoderTest.java
index 258eb0fce..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 {
diff --git a/src/test/java/org/traccar/protocol/SmokeyProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SmokeyProtocolDecoderTest.java
index de025c18e..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 {
diff --git a/src/test/java/org/traccar/protocol/SolarPoweredProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SolarPoweredProtocolDecoderTest.java
index 445628f6d..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;
diff --git a/src/test/java/org/traccar/protocol/SpotProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SpotProtocolDecoderTest.java
index 03d0e97f0..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 {
diff --git a/src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java
index 0a6ad0163..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;
diff --git a/src/test/java/org/traccar/protocol/StarcomProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/StarcomProtocolDecoderTest.java
index 84c470970..9f11db2e7 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 {
@@ -11,6 +11,9 @@ public class StarcomProtocolDecoderTest extends ProtocolTest {
var decoder = inject(new StarcomProtocolDecoder(null));
verifyPosition(decoder, text(
+ "|unit=579978,unittype=5,address=196.190.61.110,kind=1,pending=0,mileage=127268.864,odometer=339863,logic_state=1,reason=20,eventid=1,response=0,longitude=40.86503,latitude=9.06824,altitude=1809,gps_valid=1,gps_connected=1,satellites=7,velocity=23,heading=130,emergency=0,driver=0,ignition=1,door=1,arm=0,disarm=0,extra1=0,extra2=0,extra3=0,siren=0,lock=0,immobilizer=0,unlock=0,fuel=0,rpm=0,modemsignal=0,main_voltage=14.11,backup_voltage=100.00,analog1=3.38,analog2=0.00,analog3=0.00,datetime_utc=2023/08/24 14:56:29,datetime_actual=2023/08/24 14:56:23,network=TCPIP 6600|\r\n"));
+
+ 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"));
verifyAttributes(decoder, text(
diff --git a/src/test/java/org/traccar/protocol/StartekFrameDecoderTest.java b/src/test/java/org/traccar/protocol/StartekFrameDecoderTest.java
new file mode 100644
index 000000000..789126471
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/StartekFrameDecoderTest.java
@@ -0,0 +1,23 @@
+package org.traccar.protocol;
+
+import org.junit.jupiter.api.Test;
+import org.traccar.ProtocolTest;
+
+public class StartekFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ var decoder = inject(new StartekFrameDecoder());
+
+ verifyFrame(
+ binary("26265c3134332c3836353439313036393537333134302c3030302c302c2c3234303130343136303031362c412c2d362e3239303633382c3130362e3830393537382c31352c302e382c302c3238322c35372c3338382c3531307c31307c303444457c30303030373442452c33312c30303030303033432c30302c30302c303444347c303141327c303030307c303030302c312c2c2c31350d0a"),
+ decoder.decode(null, null, binary("26265c3134332c3836353439313036393537333134302c3030302c302c2c3234303130343136303031362c412c2d362e3239303633382c3130362e3830393537382c31352c302e382c302c3238322c35372c3338382c3531307c31307c303444457c30303030373442452c33312c30303030303033432c30302c30302c303444347c303141327c303030307c303030302c312c2c2c31350d0a")));
+
+ verifyFrame(
+ binary("26265c3534362c3836353439313036313134353937302c3731302c54312c302e302c302e302c302e302c302e302c302c302c302c302c302e302c302c302e302c302c46302c302e302c302e302c302e302c302e302c302e302c302c302e302c302c302c302c302c302c312c302c302e302c30302c302e302c302e302c302e302c300d0a54322c302e3030302c302e302c393232333337323033363835343737353830382e382c393232333337323033363835343737353830382e382c343239343936373239352c343239343936373239352c302c3432393439363732392c302c302c302c302c302c302c302c302c302c302c302e302c32313437343833362e302c393232333337323033363835343737353830382e382c302c30302c302c302c302e30300d0a54352c302c302c302c302c302c302c302c302c302c302c302c302c302e302c302e302c2a2c2a2c300d0a54362c30302c30332c30302c31462c31462c31462c30452c30332c30302c30302c30302c30302c31462c31460d0a54372c302c302c302c3432393439363732392c32313437343833362e3030302c3432393439363732392c302e3030302c302c302e3030302c302c302e3030302c302c302e3030302c302c302e3030302c302c302e3030302c302c302e3030302c302c302e3030302c302c302e3030302c302c302c302c302e3030300d0a54782c2a2c2a2c2a2c2a2c2a2c302e302c302c302c302c302c2d3132352c302c302c302c302c302c302c300d0a46330d0a"),
+ decoder.decode(null, null, binary("26265c3534362c3836353439313036313134353937302c3731302c54312c302e302c302e302c302e302c302e302c302c302c302c302c302e302c302c302e302c302c46302c302e302c302e302c302e302c302e302c302e302c302c302e302c302c302c302c302c302c312c302c302e302c30302c302e302c302e302c302e302c300d0a54322c302e3030302c302e302c393232333337323033363835343737353830382e382c393232333337323033363835343737353830382e382c343239343936373239352c343239343936373239352c302c3432393439363732392c302c302c302c302c302c302c302c302c302c302c302e302c32313437343833362e302c393232333337323033363835343737353830382e382c302c30302c302c302c302e30300d0a54352c302c302c302c302c302c302c302c302c302c302c302c302c302e302c302e302c2a2c2a2c300d0a54362c30302c30332c30302c31462c31462c31462c30452c30332c30302c30302c30302c30302c31462c31460d0a54372c302c302c302c3432393439363732392c32313437343833362e3030302c3432393439363732392c302e3030302c302c302e3030302c302c302e3030302c302c302e3030302c302c302e3030302c302c302e3030302c302c302e3030302c302c302e3030302c302c302e3030302c302c302c302c302e3030300d0a54782c2a2c2a2c2a2c2a2c2a2c302e302c302c302c302c302c2d3132352c302c302c302c302c302c302c300d0a46330d0a")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/StartekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/StartekProtocolDecoderTest.java
index 072c19942..0d9516256 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;
@@ -11,42 +11,58 @@ public class StartekProtocolDecoderTest extends ProtocolTest {
var decoder = inject(new StartekProtocolDecoder(null));
+ verifyAttributes(decoder, text(
+ "&&\\546,865491061145970,710,T1,0.0,0.0,0.0,0.0,0,0,0,0,0.0,0,0.0,0,F0,0.0,0.0,0.0,0.0,0.0,0,0.0,0,0,0,0,0,1,0,0.0,00,0.0,0.0,0.0,0\r\n",
+ "T2,0.000,0.0,9223372036854775808.8,9223372036854775808.8,4294967295,4294967295,0,429496729,0,0,0,0,0,0,0,0,0,0,0.0,21474836.0,9223372036854775808.8,0,00,0,0,0.00\r\n",
+ "T5,0,0,0,0,0,0,0,0,0,0,0,0,0.0,0.0,*,*,0\r\n",
+ "T6,00,03,00,1F,1F,1F,0E,03,00,00,00,00,1F,1F\r\n",
+ "T7,0,0,0,429496729,21474836.000,429496729,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0,0,0.000\r\n",
+ "Tx,*,*,*,*,*,0.0,0,0,0,0,-125,0,0,0,0,0,0,0\r\n",
+ "F3\r\n"));
+
+ verifyPosition(decoder, text(
+ "&&l141,863911061945394,000,0,,230918072531,A,22.678598,114.045970,26,0.6,0,0,74,2286304571,460|0|249F|00001093,20,001C,00,00,04A7|019C|0000|0000,1,C0\r\n"));
+
+ 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\r\n"),
+ 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"),
+ "&&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\r\n"),
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"),
+ "&&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\r\n"),
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"));
+ "&&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\r\n"));
verifyPosition(decoder, text(
- "&&W149,865429043319537,000,0,,211103013512,A,22.679003,114.045085,16,1.1,0,271,76,109075,460|0|249F|000010C5,19,0000003E,00,00,0A57|0168|0000|0000,1,0100000C"));
+ "&&W149,865429043319537,000,0,,211103013512,A,22.679003,114.045085,16,1.1,0,271,76,109075,460|0|249F|000010C5,19,0000003E,00,00,0A57|0168|0000|0000,1,0100000C\r\n"));
verifyAttribute(decoder, text(
- "&&:23,860262050015424,129,OKA2"),
- Position.KEY_RESULT, "129,OK");
+ "&&:23,860262050015424,129,OKA2\r\n"),
+ Position.KEY_RESULT, "OK");
verifyPosition(decoder, text(
- "&&X152,861157040151686,000,18,,210907163833,A,10.232715,-67.880423,11,1.4,0,275,437,34804,734|2|3EE4|00579406,28,00000015,00,00,0000|017D|0000|0000,1,010000,,9A"));
+ "&&X152,861157040151686,000,18,,210907163833,A,10.232715,-67.880423,11,1.4,0,275,437,34804,734|2|3EE4|00579406,28,00000015,00,00,0000|017D|0000|0000,1,010000,,9A\r\n"));
verifyPosition(decoder, text(
- "&&o125,861157040554384,000,0,,210702235150,A,27.263505,153.037061,11,1.2,0,0,31,5125,505|1|7032|8C89802,20,0000002D,00,00,01E2|019DF0"));
+ "&&o125,861157040554384,000,0,,210702235150,A,27.263505,153.037061,11,1.2,0,0,31,5125,505|1|7032|8C89802,20,0000002D,00,00,01E2|019DF0\r\n"));
verifyAttribute(decoder, text(
- "&&a152,860262050010565,000,53,8F5300,210528015706,A,-38.229746,145.043446,6,1.5,0,285,84,2102994,505|1|306E|082D6101,31,0000003D,02,02,04C0|01A0|0000|0000,1,,DC"),
+ "&&a152,860262050010565,000,53,8F5300,210528015706,A,-38.229746,145.043446,6,1.5,0,285,84,2102994,505|1|306E|082D6101,31,0000003D,02,02,04C0|01A0|0000|0000,1,,DC\r\n"),
Position.KEY_DRIVER_UNIQUE_ID, "8F5300");
verifyPosition(decoder, text(
- "&&>141,860262050010565,000,36,,210407094323,V,-38.229711,145.043161,0,0.0,0,0,0,14222,505|1|306E|082D6115,24,00000039,00,00,04C0|0164|0000|0000,1,,41"));
+ "&&>141,860262050010565,000,36,,210407094323,V,-38.229711,145.043161,0,0.0,0,0,0,14222,505|1|306E|082D6115,24,00000039,00,00,04C0|0164|0000|0000,1,,41\r\n"));
verifyPosition(decoder, text(
- "&&A147,021104023195429,000,0,,180106093046,A,22.646430,114.065730,8,0.9,54,86,76,326781,460|0|27B3|0EA7,27,0000000F,02,01,04E2|018C|01C8|0000,1,0104B0,01013D|02813546"));
+ "&&A147,021104023195429,000,0,,180106093046,A,22.646430,114.065730,8,0.9,54,86,76,326781,460|0|27B3|0EA7,27,0000000F,02,01,04E2|018C|01C8|0000,1,0104B0,01013D|02813546\r\n"));
verifyPosition(decoder, text(
- "&&y139,860262050009146,000,0,,210323131512,A,22.678655,114.046223,14,1.1,0,231,71,5,460|0|249F|0099C257,28,0000003D,00,00,0493|0199|0000|0000,1,,33"));
+ "&&y139,860262050009146,000,0,,210323131512,A,22.678655,114.046223,14,1.1,0,231,71,5,460|0|249F|0099C257,28,0000003D,00,00,0493|0199|0000|0000,1,,33\r\n"));
}
diff --git a/src/test/java/org/traccar/protocol/StartekProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/StartekProtocolEncoderTest.java
index f04d0cb67..c87e421f2 100644
--- a/src/test/java/org/traccar/protocol/StartekProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/StartekProtocolEncoderTest.java
@@ -1,10 +1,10 @@
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 {
diff --git a/src/test/java/org/traccar/protocol/StbProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/StbProtocolDecoderTest.java
index c618ac21c..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 {
diff --git a/src/test/java/org/traccar/protocol/Stl060ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Stl060ProtocolDecoderTest.java
index 3ef4b1901..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 {
diff --git a/src/test/java/org/traccar/protocol/SuntechFrameDecoderTest.java b/src/test/java/org/traccar/protocol/SuntechFrameDecoderTest.java
index 6d0351a8e..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 {
diff --git a/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java
index 107c03d36..d656bba13 100644
--- a/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.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;
import org.traccar.model.Position;
@@ -12,6 +12,9 @@ public class SuntechProtocolDecoderTest extends ProtocolTest {
var decoder = inject(new SuntechProtocolDecoder(null));
+ 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(
"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;;;;"));
@@ -209,7 +212,7 @@ public class SuntechProtocolDecoderTest extends ProtocolTest {
}
- @Ignore
+ @Disabled
@Test
public void testDecodeCrash() throws Exception {
@@ -230,13 +233,13 @@ public class SuntechProtocolDecoderTest extends ProtocolTest {
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"),
+ "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;:;:"),
+ "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(
@@ -259,7 +262,7 @@ public class SuntechProtocolDecoderTest extends ProtocolTest {
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"),
+ "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);
}
@@ -272,7 +275,7 @@ public class SuntechProtocolDecoderTest extends ProtocolTest {
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"),
+ "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);
}
@@ -283,13 +286,19 @@ public class SuntechProtocolDecoderTest extends ProtocolTest {
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"),
+ "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"),
+ "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");
+ decoder.setHbm(true);
+
+ verifyAttribute(decoder, buffer(
+ "ST300STT;807469112;45;315;20231215;15:25:03;104147;-16.030168;-047.989150;000.000;000.00;19;1;8600;12.14;000010;1;0456;000373;4.1;1;01B54221010000;0"),
+ Position.KEY_DRIVER_UNIQUE_ID, "01B54221010000");
+
}
}
diff --git a/src/test/java/org/traccar/protocol/SupermateProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SupermateProtocolDecoderTest.java
index e96c9b62d..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 {
diff --git a/src/test/java/org/traccar/protocol/SviasProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SviasProtocolDecoderTest.java
index fb9053706..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 {
diff --git a/src/test/java/org/traccar/protocol/SwiftechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SwiftechProtocolDecoderTest.java
index 923b7abfb..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 {
diff --git a/src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java
index 1622f64f2..91020714c 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;
@@ -11,10 +11,22 @@ public class T55ProtocolDecoderTest extends ProtocolTest {
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(
+ "$GNRMC,164414.90,A,4650.5156500,N,01246.1059604,E,0.018,,091123,,,A,V*15"));
+
+ verifyPosition(decoder, text(
+ "$GNGGA,164414.90,4650.5156500,N,01246.1059604,E,1,12,0.84,740.729,M,44.804,M,,*4E"));
+
+ verifyNull(decoder, text(
+ "$GNGLL,4650.5156500,N,01246.1059604,E,164414.90,A,A*77"));
+
+ verifyPosition(decoder, text(
"QZE,868994033976700,35,28062020,113553,22.13673,114.57263,0,22,A,0"));
verifyNull(decoder, text(
diff --git a/src/test/java/org/traccar/protocol/T57FrameDecoderTest.java b/src/test/java/org/traccar/protocol/T57FrameDecoderTest.java
index 40d5bc9e9..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 {
diff --git a/src/test/java/org/traccar/protocol/T57ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/T57ProtocolDecoderTest.java
index 2e097562b..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 {
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 10d0aad59..468751e1b 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;
@@ -12,6 +12,12 @@ public class T800xProtocolDecoderTest extends ProtocolTest {
var decoder = inject(new T800xProtocolDecoder(null));
verifyAttributes(decoder, binary(
+ "25251300594a1b0869738060144917003c0e101e03e85a2dc8c00005070000410000000000000000000000005b000003a5b45e00230919102252e3a5094288fabfc0e98b15420000010403921352ffff0000001cffffffffff25251300594a1c0869738060144917003c0e101e03e85a2ac7c00005070000410000000000000000000000002d000003a5b48b002309191023522d320642abfebfc0e98b15420000010c03921345ffff0000001bffffffffff25251300594a1d0869738060144917003c0e101e03e85a1ac9c000050700004100000000000000000000000024000003a5b4af00230919102452b81ef9410002c0c0ec8b15420108011403911345ffff0000001dffffffffff25251300594a1e0869738060144917003c0e101e03e85a3ec7c00005070000410000000000000000000000000e000003a5b4bd002309191025060ad7ec41da02c0c0058c15420084016303921345ffff0000001cffffffffff25251300594a1f0869738060144917003c0e101e03e85a3ec7c020050700004100000000000000000000000005000003a5b4c2002309191025090e2deb410203c0c0108c15420089014303921338ffff0000001dffffffffff25251300594a200869738060144917003c0e101e03e85a1ec5c000050700004100000000000000000000000020000003a5b4e20023091910260948e1bc412205c0c0458c15420040013603921355ffff0000001bffffffffff25251300594a210869738060144917003c0e101e03e85a00c5c020050700004100000000000000000000000000000003a5b4e20023091910270948e1bc412205c0c0458c15420040013603911332ffff0000001dffffffffff"));
+
+ verifyAttributes(decoder, binary(
+ "272704004901380864112055585747c612230321220006000036435fc8acc2ee600f420000000000000000909019003900001356a18000012c0000a8c00000001e20d4800000c00000"));
+
+ verifyAttributes(decoder, binary(
"2525110055000208677300508924902206262035310c540045004c00430045004c0004454447450847534d20313930300f323134303734323036373835323839143839333430373131373930303936383037363846"));
verifyAttributes(decoder, binary(
diff --git a/src/test/java/org/traccar/protocol/T800xProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/T800xProtocolEncoderTest.java
index b2d7c57a2..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;
diff --git a/src/test/java/org/traccar/protocol/TaipProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TaipProtocolDecoderTest.java
index 36c9d9148..197e30c15 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;
@@ -11,6 +11,18 @@ public class TaipProtocolDecoderTest extends ProtocolTest {
var decoder = inject(new TaipProtocolDecoder(null));
+ verifyAttributes(decoder, text(
+ ">RUS00,010170000000+0000000+000000000000001009999000011060074755268EF,0001139503871486,01,ZZZZZZZZZZ;ID=11817;#LOG:6AE4;*2C<"));
+
+ verifyPosition(decoder, text(
+ ">RPI041220132203-2683525-065204060150001050000101511140022118857EF27;ID=0000;#LOG:DECB;*07<"));
+
+ verifyPosition(decoder, text(
+ ">RCQ00151123235718-2782354-06407582055121FF0013501CDCC6313011100001514;#0805;ID=SIA056;*15<"));
+
+ verifyNull(decoder, text(
+ ">RTT151123153149-4330468-06503640000009300DF2101 04101203 000 00000000130000040414;ID=8803;#1ABD;*2B<"));
+
verifyAttribute(decoder, text(
">RUS00,111220124402-3138067-06417623000012200FF,000000000000000000000000000,0000000111,15640422,00000,+25.5,00000,51;ID=CST3G0443;#IP1:089F;*34<"),
Position.PREFIX_TEMP + 1, 25.5);
diff --git a/src/test/java/org/traccar/protocol/TechTltProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TechTltProtocolDecoderTest.java
index b4617cb61..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 {
diff --git a/src/test/java/org/traccar/protocol/TechtoCruzFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TechtoCruzFrameDecoderTest.java
index 36c3b578b..1e1f8d431 100644
--- a/src/test/java/org/traccar/protocol/TechtoCruzFrameDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/TechtoCruzFrameDecoderTest.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 TechtoCruzFrameDecoderTest extends ProtocolTest {
diff --git a/src/test/java/org/traccar/protocol/TechtoCruzProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TechtoCruzProtocolDecoderTest.java
index 459401469..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 {
diff --git a/src/test/java/org/traccar/protocol/TekFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TekFrameDecoderTest.java
index 98b2b80c4..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 {
diff --git a/src/test/java/org/traccar/protocol/TekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TekProtocolDecoderTest.java
index 17910a8d6..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 {
diff --git a/src/test/java/org/traccar/protocol/TelemaxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TelemaxProtocolDecoderTest.java
index 9f36b3f96..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 {
diff --git a/src/test/java/org/traccar/protocol/TelicFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TelicFrameDecoderTest.java
index dc6cc58c6..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 {
diff --git a/src/test/java/org/traccar/protocol/TelicProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TelicProtocolDecoderTest.java
index 214bb06c7..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 {
diff --git a/src/test/java/org/traccar/protocol/TeltonikaFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TeltonikaFrameDecoderTest.java
index adc768460..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 {
diff --git a/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java
index ba64642f2..2a8ff87b6 100644
--- a/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java
@@ -1,8 +1,9 @@
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 {
@@ -15,6 +16,17 @@ public class TeltonikaProtocolDecoderTest extends ProtocolTest {
"000F313233343536373839303132333435"));
verifyPositions(decoder, binary(
+ "00000000000000728e010000018b23dd796300fbf7263c24f9e11a0000000000000002240001000000000000000000010224004501210001e50110cde39f7e42bb55aa788e4a29ed650055020ab70a8f264c6000ffff6b210001b00110f89b907e42bb55aaa3463b29ed650055020ab708bb2600ae0500096c01000051d4"));
+
+ verifyAttribute(decoder, binary(
+ "000000000000004b8e010000018368952793000f0e54fc209ab05800b300b40e00002a4f0001000000000000000000012a4f001e011c0001a40110eb47706aa38255aa96f21a154e2d00550d01000e020bd6010000823f"),
+ "tag1Battery", 3030);
+
+ verifyAttribute(decoder, binary(
+ "00000000000000240d01060000001c642b3ad14754534c7c367c317c307c31323734393838347c317c0d0a010000ec11"),
+ Position.KEY_DRIVER_UNIQUE_ID, "12749884");
+
+ verifyPositions(decoder, binary(
"00000000000000a28e0100000183ac617e3001123eb99b1e142db4000000000000000000001d000900f000005000001503004500011e1801212d01242a012722012a18001100b5000000b600000018000000cd151000431c2d011f6981012047d701226981012347d901256981012647d8012869810129e6f304b0000304b1000304b2000304b30003000100f10000639d0002000b0000000214bf12fe000e0000000029d18c95000001000051b6"));
verifyPositions(decoder, binary(
@@ -158,7 +170,7 @@ public class TeltonikaProtocolDecoderTest extends ProtocolTest {
}
- @Ignore
+ @Disabled
@Test
public void testDecodeConnectionless() throws Exception {
diff --git a/src/test/java/org/traccar/protocol/TeltonikaProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/TeltonikaProtocolEncoderTest.java
index d7e1149aa..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;
diff --git a/src/test/java/org/traccar/protocol/TeraTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TeraTrackProtocolDecoderTest.java
index fc66f53bb..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 {
diff --git a/src/test/java/org/traccar/protocol/ThinkPowerProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ThinkPowerProtocolDecoderTest.java
index 2085112ec..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 {
diff --git a/src/test/java/org/traccar/protocol/ThinkRaceProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ThinkRaceProtocolDecoderTest.java
index 859dd4f89..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 {
diff --git a/src/test/java/org/traccar/protocol/ThurayaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ThurayaProtocolDecoderTest.java
index 90431fa24..b9bc1dbc2 100644
--- a/src/test/java/org/traccar/protocol/ThurayaProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/ThurayaProtocolDecoderTest.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 ThurayaProtocolDecoderTest extends ProtocolTest {
diff --git a/src/test/java/org/traccar/protocol/Tk102ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tk102ProtocolDecoderTest.java
index 3bcc9994a..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 {
diff --git a/src/test/java/org/traccar/protocol/Tk103FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Tk103FrameDecoderTest.java
index 87c3d9317..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 {
diff --git a/src/test/java/org/traccar/protocol/Tk103ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tk103ProtocolDecoderTest.java
index 8b3177136..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;
@@ -12,6 +12,13 @@ public class Tk103ProtocolDecoderTest extends ProtocolTest {
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(
diff --git a/src/test/java/org/traccar/protocol/Tk103ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Tk103ProtocolEncoderTest.java
index 359e432c7..57ebbaec1 100644
--- a/src/test/java/org/traccar/protocol/Tk103ProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/Tk103ProtocolEncoderTest.java
@@ -1,10 +1,10 @@
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 {
diff --git a/src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java
index a6a9f836e..085d6fc5b 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;
@@ -11,7 +11,28 @@ public class Tlt2hProtocolDecoderTest extends ProtocolTest {
var decoder = inject(new Tlt2hProtocolDecoder(null));
- verifyNull(decoder, text(
+ verifyPositions(decoder, false, text(
+ "#862255061752835#MT710#0000#AUTO#1\r\n" +
+ "#4106#$GPRMC,151410.00,A,3010.4103,N,08146.2728,W,,214.90,010324,,,A*58\r\n"));
+
+ verifyPositions(decoder, text(
+ "#868105044690301#MT600+#0000#0#0#143#40#0#AUTO#1\r\n",
+ "#072030fcf21$GPRMC,155616.00,A,4931.9210,N,09652.5290,W,53.80,90.00,150224,,,A*48\r\n"));
+
+ verifyAttribute(decoder, text(
+ "#867665041689485#MT700N#0000#HT#1\r\n",
+ "#5065$GPRMC,148996.00,A,2485.2458,N,01258.4535,E,,,151348,,,A*5D\r\n"),
+ Position.KEY_BATTERY, 5.065);
+
+ verifyPositions(decoder, false, text(
+ "#862255061983166#MT700NW#0000#TOWED#1\r\n",
+ "#4502$WIFI,051550.00,A,-50,7683C2CBC0B0,-51,7683C29BC0B0,-51,7683C2BBC0B0,-51,7483C2DBC0B0,-51,7683C2ABC0B0,221123*78\r\n"));
+
+ verifyPositions(decoder, false, text(
+ "#862255061825896#MT710#0000#TOWED#1\r\n",
+ "#39#$WIFI,015259.00,A,-47,7483C2DBC0B0,-48,7683C2ABC0B0,-48,7683C29BC0B0,-48,7683C2CBC0B0,-48,7683C2BBC0B0,151123*74\r\n"));
+
+ verifyPositions(decoder, false, text(
"#860517049471362#MT700#0000#AUTO#1\r\n",
"#36$GPRMC,,V,,,,,,,,,,A*5C\r\n"));
diff --git a/src/test/java/org/traccar/protocol/TlvProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TlvProtocolDecoderTest.java
index d6d1b7275..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 {
diff --git a/src/test/java/org/traccar/protocol/TmgFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TmgFrameDecoderTest.java
index 9cdcb6169..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 {
diff --git a/src/test/java/org/traccar/protocol/TmgProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TmgProtocolDecoderTest.java
index 6d3c36005..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 {
diff --git a/src/test/java/org/traccar/protocol/TopflytechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TopflytechProtocolDecoderTest.java
index b49345a42..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 {
diff --git a/src/test/java/org/traccar/protocol/TopinProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TopinProtocolDecoderTest.java
index 5e7ec1136..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;
@@ -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 a69f389ac..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;
diff --git a/src/test/java/org/traccar/protocol/TotemFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TotemFrameDecoderTest.java
index 175c32848..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 {
diff --git a/src/test/java/org/traccar/protocol/TotemProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TotemProtocolDecoderTest.java
index df5734568..949e69275 100644
--- a/src/test/java/org/traccar/protocol/TotemProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/TotemProtocolDecoderTest.java
@@ -1,7 +1,8 @@
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 {
@@ -10,6 +11,10 @@ public class TotemProtocolDecoderTest extends ProtocolTest {
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"));
diff --git a/src/test/java/org/traccar/protocol/TotemProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/TotemProtocolEncoderTest.java
index 97a044b51..795f75842 100644
--- a/src/test/java/org/traccar/protocol/TotemProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/TotemProtocolEncoderTest.java
@@ -1,10 +1,10 @@
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 {
diff --git a/src/test/java/org/traccar/protocol/Tr20ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tr20ProtocolDecoderTest.java
index e4917c872..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 {
diff --git a/src/test/java/org/traccar/protocol/Tr900ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tr900ProtocolDecoderTest.java
index 96ddf4175..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 {
diff --git a/src/test/java/org/traccar/protocol/TrackboxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TrackboxProtocolDecoderTest.java
index 10603db1c..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 {
diff --git a/src/test/java/org/traccar/protocol/TrakMateProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TrakMateProtocolDecoderTest.java
index 7542b3456..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 {
diff --git a/src/test/java/org/traccar/protocol/TramigoFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TramigoFrameDecoderTest.java
index a093d94e9..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 {
@@ -11,6 +11,10 @@ public class TramigoFrameDecoderTest extends ProtocolTest {
var decoder = inject(new TramigoFrameDecoder());
verifyFrame(
+ binary("0480001df35b1b69101a023ef34f0090436d38003200380e0000850081c0e4ff6d542f00000015000000050000000000007600a20100008f436d3800014400000000000000000021000a0006005a574a6169726f7320486972692043656e747265205072696d617279205363686f6f6c536f7574686572746f6e486172617265"),
+ decoder.decode(null, null, binary("0480001df35b1b69101a023ef34f0090436d38003200380e0000850081c0e4ff6d542f00000015000000050000000000007600a20100008f436d3800014400000000000000000021000a0006005a574a6169726f7320486972692043656e747265205072696d617279205363686f6f6c536f7574686572746f6e486172617265")));
+
+ verifyFrame(
binary("8000ed2bb0009c000101bee000050b09633d925b5472616d69676f3a205472697020737461727465642c2053686f636b2053656e736f722c206174204b696e6720437265656b20526f61642d46726565746f776e205374726565742c20506f727420486172636f7572742c205269766572732c204e472c20342e37363336312c20372e30313836382c2030373a31383a333620536570203320454f46"),
decoder.decode(null, null, binary("8000ed2bb0009c000101bee000050b09633d925b5472616d69676f3a205472697020737461727465642c2053686f636b2053656e736f722c206174204b696e6720437265656b20526f61642d46726565746f776e205374726565742c20506f727420486172636f7572742c205269766572732c204e472c20342e37363336312c20372e30313836382c2030373a31383a333620536570203320454f46")));
diff --git a/src/test/java/org/traccar/protocol/TramigoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TramigoProtocolDecoderTest.java
index c2d13d199..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 {
@@ -10,6 +10,12 @@ public class TramigoProtocolDecoderTest extends ProtocolTest {
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 6dc28d89e..370775735 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 {
@@ -14,6 +14,9 @@ public class TrvProtocolDecoderTest extends ProtocolTest {
"TRVAP00352121088015548"));
verifyPosition(decoder, text(
+ "TRVYP14080524A2232.9806N11404.9355E000.1061830323.870600090800010200011,460,0,9520,3671,Home|74-DE-2B-44-88-8C|97&Home1|74-DE-2B-44-88-8C|97&Home2|74-DE-2B-44-88-8C|97& Home3|74-DE-2B-44-88-8C|97"));
+
+ verifyPosition(decoder, text(
"TRVYP03190805A1828.9242N07353.9423E000.0150716029.0010000810020201112,404,27,184,10229"));
verifyNotNull(decoder, text(
@@ -66,7 +69,7 @@ public class TrvProtocolDecoderTest extends ProtocolTest {
"TRVAP10080524A2232.9806N11404.9355E000.1061830323.8706000908000502,460,0,9520,3671,00,zh-cn,00"));
verifyPosition(decoder, text(
- "TRVYP14220217A5235.7885N00724.1840E000.0130919177.561000050660000200004,262,01,14635,52789,FritzBox7|DC-39-8F-7E-94-73|-89&FritzBox7|24-4E-5D-71-C3-9C|-90&MY_IOT|80-B4-F7-77-9C-7C|-81&MYAP|44-D4-F7-77-9C-7C|-80#"));
+ "TRVYP14220217A5235.7885N00724.1840E000.0130919177.561000050660000200004,262,01,14635,52789,FritzBox7|DC-39-8F-7E-94-73|-89&FritzBox7|24-4E-5D-71-C3-9C|-90&MY_IOT|80-B4-F7-77-9C-7C|-81&MYAP|44-D4-F7-77-9C-7C|-80#"));
}
diff --git a/src/test/java/org/traccar/protocol/Tt8850ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tt8850ProtocolDecoderTest.java
index 3f5c60daa..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 {
diff --git a/src/test/java/org/traccar/protocol/TytanProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TytanProtocolDecoderTest.java
index de6f3a6ff..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 {
diff --git a/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java
index 4a861fb06..d90e5e07e 100644
--- a/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java
@@ -1,7 +1,8 @@
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 {
@@ -10,6 +11,10 @@ public class TzoneProtocolDecoderTest extends ProtocolTest {
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"));
diff --git a/src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java b/src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java
index 534287e0a..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 {
diff --git a/src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java
index 52f2520cc..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 {
diff --git a/src/test/java/org/traccar/protocol/UlbotechProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/UlbotechProtocolEncoderTest.java
index 50e9321ce..0ee1516ba 100644
--- a/src/test/java/org/traccar/protocol/UlbotechProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/UlbotechProtocolEncoderTest.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;
diff --git a/src/test/java/org/traccar/protocol/UproProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/UproProtocolDecoderTest.java
index 8d32eebb1..f070c6201 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;
@@ -83,6 +83,14 @@ public class UproProtocolDecoderTest extends ProtocolTest {
verifyPosition(decoder, buffer(
"*AI2000905300036,AD1&A1703054913231101844949860000251115&B0500000000&C0;4?72:9&F0000"));
+ verifyAttribute(decoder, buffer(
+ "*HQ200862312328000001,AD1&A1520441548253003503696640017270124&B0000000000&C00000117&F0000&R2118&N01&V0125&X(J01E0)&K00300&Z000&d01286"),
+ Position.PREFIX_ADC + 1, 12.86);
+
+ verifyAttribute(decoder, buffer(
+ "*HQ200862312328000001,BA&A1520461548253003503696640017270124&B0000000000&C00000117&F0000&R2218&N01&V0125&X(J01E0)&K00300&Z000&d01287"),
+ Position.PREFIX_ADC + 1, 12.87);
+
}
}
diff --git a/src/test/java/org/traccar/protocol/UuxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/UuxProtocolDecoderTest.java
index 40776278d..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 {
diff --git a/src/test/java/org/traccar/protocol/V680ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/V680ProtocolDecoderTest.java
index 105dc8339..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 {
diff --git a/src/test/java/org/traccar/protocol/ValtrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ValtrackProtocolDecoderTest.java
new file mode 100644
index 000000000..d526ea850
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ValtrackProtocolDecoderTest.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 ValtrackProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ var decoder = inject(new ValtrackProtocolDecoder(null));
+
+ verifyPositions(decoder, request(HttpMethod.POST, "/", buffer(
+ "{\"resource\":[{\"devid\":\"869731054075783\",\"etype\":\"G_PING\",\"lat\":\"0.000000\",\"lon\":\"0.000000\",\"vbat\":\"12.263848\",\"speed\":\"\",\"nlat\":\"4255.364258\",\"nlon\":\"176.867203\",\"ncsq\":\"16,99\"},{\"devid\":\"869731054075783\",\"etype\":\"G_PING\",\"lat\":\"0.000000\",\"lon\":\"0.000000\",\"vbat\":\"12.263848\",\"speed\":\"\",\"nlat\":\"4255.364258\",\"nlon\":\"176.867203\",\"ncsq\":\"16,99\"},{\"devid\":\"869731054075783\",\"etype\":\"G_PING\",\"lat\":\"0.000000\",\"lon\":\"0.000000\",\"vbat\":\"12.263848\",\"speed\":\"\",\"nlat\":\"4255.364258\",\"nlon\":\"176.867203\",\"ncsq\":\"16,99\"}]}")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/VisiontekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/VisiontekProtocolDecoderTest.java
index 042b66cae..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 {
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 25cc03781..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 {
diff --git a/src/test/java/org/traccar/protocol/Vt200FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Vt200FrameDecoderTest.java
index 7d039dc8f..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 {
diff --git a/src/test/java/org/traccar/protocol/Vt200ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Vt200ProtocolDecoderTest.java
index 25ce5550a..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;
diff --git a/src/test/java/org/traccar/protocol/VtfmsFrameDecoderTest.java b/src/test/java/org/traccar/protocol/VtfmsFrameDecoderTest.java
index e381153a2..18baf75cb 100644
--- a/src/test/java/org/traccar/protocol/VtfmsFrameDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/VtfmsFrameDecoderTest.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 VtfmsFrameDecoderTest extends ProtocolTest {
diff --git a/src/test/java/org/traccar/protocol/VtfmsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/VtfmsProtocolDecoderTest.java
index 4925d9769..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 {
diff --git a/src/test/java/org/traccar/protocol/WatchFrameDecoderTest.java b/src/test/java/org/traccar/protocol/WatchFrameDecoderTest.java
index 42464c6fe..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 {
@@ -11,6 +11,14 @@ public class WatchFrameDecoderTest extends ProtocolTest {
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"),
decoder.decode(null, null, binary("5b33472a3335323636313039303134333135302a303030412a4c4b2c302c302c3130305d")));
diff --git a/src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java
index 37fab7e40..5fd0ede44 100644
--- a/src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.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.database.MediaManager;
import org.traccar.model.Position;
@@ -17,6 +17,13 @@ public class WatchProtocolDecoderTest extends ProtocolTest {
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]"));
diff --git a/src/test/java/org/traccar/protocol/WatchProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/WatchProtocolEncoderTest.java
index c83c103c3..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;
diff --git a/src/test/java/org/traccar/protocol/WialonProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/WialonProtocolDecoderTest.java
index 6953784fb..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 {
@@ -13,6 +13,10 @@ public class WialonProtocolDecoderTest extends ProtocolTest {
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 45c86ae1a..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 {
diff --git a/src/test/java/org/traccar/protocol/WliProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/WliProtocolDecoderTest.java
index e74b1df06..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 {
diff --git a/src/test/java/org/traccar/protocol/WondexFrameDecoderTest.java b/src/test/java/org/traccar/protocol/WondexFrameDecoderTest.java
index a1cbfe737..f1f65cb2c 100644
--- a/src/test/java/org/traccar/protocol/WondexFrameDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/WondexFrameDecoderTest.java
@@ -1,10 +1,10 @@
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 {
diff --git a/src/test/java/org/traccar/protocol/WondexProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/WondexProtocolDecoderTest.java
index 72ba8e163..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 {
diff --git a/src/test/java/org/traccar/protocol/WondexProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/WondexProtocolEncoderTest.java
index f482871dd..2e1a45c8c 100644
--- a/src/test/java/org/traccar/protocol/WondexProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/WondexProtocolEncoderTest.java
@@ -1,10 +1,10 @@
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
diff --git a/src/test/java/org/traccar/protocol/WristbandProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/WristbandProtocolDecoderTest.java
index b901820fe..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 {
diff --git a/src/test/java/org/traccar/protocol/Xexun2FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Xexun2FrameDecoderTest.java
index 34437862c..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 {
diff --git a/src/test/java/org/traccar/protocol/Xexun2FrameEncoderTest.java b/src/test/java/org/traccar/protocol/Xexun2FrameEncoderTest.java
index d327930b5..27e894705 100644
--- a/src/test/java/org/traccar/protocol/Xexun2FrameEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/Xexun2FrameEncoderTest.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 Xexun2FrameEncoderTest extends ProtocolTest {
diff --git a/src/test/java/org/traccar/protocol/Xexun2ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Xexun2ProtocolDecoderTest.java
index 48ba1a691..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 {
diff --git a/src/test/java/org/traccar/protocol/Xexun2ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Xexun2ProtocolEncoderTest.java
index 483bc85fa..62d511c0b 100644
--- a/src/test/java/org/traccar/protocol/Xexun2ProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/Xexun2ProtocolEncoderTest.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;
diff --git a/src/test/java/org/traccar/protocol/XexunFrameDecoderTest.java b/src/test/java/org/traccar/protocol/XexunFrameDecoderTest.java
index 9bc39fc97..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 {
diff --git a/src/test/java/org/traccar/protocol/XexunProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/XexunProtocolDecoderTest.java
index f7599b4c3..7331827f0 100644
--- a/src/test/java/org/traccar/protocol/XexunProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/XexunProtocolDecoderTest.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 XexunProtocolDecoderTest extends ProtocolTest {
diff --git a/src/test/java/org/traccar/protocol/XirgoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/XirgoProtocolDecoderTest.java
index db9c829aa..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 {
diff --git a/src/test/java/org/traccar/protocol/XirgoProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/XirgoProtocolEncoderTest.java
index 76e2f960d..2efedf51a 100644
--- a/src/test/java/org/traccar/protocol/XirgoProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/XirgoProtocolEncoderTest.java
@@ -1,10 +1,10 @@
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 {
diff --git a/src/test/java/org/traccar/protocol/Xrb28ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Xrb28ProtocolDecoderTest.java
index 8ed175a74..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 {
diff --git a/src/test/java/org/traccar/protocol/Xrb28ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Xrb28ProtocolEncoderTest.java
index a66efecc2..eaa29a833 100644
--- a/src/test/java/org/traccar/protocol/Xrb28ProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/Xrb28ProtocolEncoderTest.java
@@ -1,10 +1,10 @@
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 {
diff --git a/src/test/java/org/traccar/protocol/Xt013ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Xt013ProtocolDecoderTest.java
index 007af984e..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 {
diff --git a/src/test/java/org/traccar/protocol/Xt2400ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Xt2400ProtocolDecoderTest.java
index 35cb3c3fa..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 {
diff --git a/src/test/java/org/traccar/protocol/YwtProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/YwtProtocolDecoderTest.java
index 81afe53a3..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 {
diff --git a/src/test/java/org/traccar/reports/ReportUtilsTest.java b/src/test/java/org/traccar/reports/ReportUtilsTest.java
index 4bf668064..0f14577fd 100644
--- a/src/test/java/org/traccar/reports/ReportUtilsTest.java
+++ b/src/test/java/org/traccar/reports/ReportUtilsTest.java
@@ -1,16 +1,16 @@
package org.traccar.reports;
import org.apache.velocity.app.VelocityEngine;
-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.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.common.TripsConfig;
import org.traccar.reports.model.StopReportItem;
import org.traccar.reports.model.TripReportItem;
import org.traccar.storage.Storage;
@@ -20,17 +20,18 @@ 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 static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-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.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;
@@ -38,10 +39,10 @@ public class ReportUtilsTest extends BaseTest {
private Storage storage;
- @Before
+ @BeforeEach
public void init() throws StorageException {
storage = mock(Storage.class);
- when(storage.getObject(any(), any())).thenReturn(mock(Device.class));
+ when(storage.getObject(eq(Device.class), any())).thenReturn(mock(Device.class));
}
private Date date(String time) throws ParseException {
@@ -62,6 +63,19 @@ public class ReportUtilsTest extends BaseTest {
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() {
@@ -78,8 +92,7 @@ public class ReportUtilsTest extends BaseTest {
@Test
public void testCalculateSpentFuel() {
ReportUtils reportUtils = new ReportUtils(
- mock(Config.class), storage, mock(PermissionsService.class),
- mock(TripsConfig.class), mock(VelocityEngine.class), null);
+ 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);
@@ -98,15 +111,15 @@ 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),
- tripsConfig, mock(VelocityEngine.class), null);
+ mock(Config.class), storage, mock(PermissionsService.class), mock(VelocityEngine.class), null);
- var trips = reportUtils.detectTripsAndStops(mock(Device.class), data, false, TripReportItem.class);
+ var trips = reportUtils.slowTripsAndStops(device, new Date(), new Date(), TripReportItem.class);
assertNotNull(trips);
assertFalse(trips.isEmpty());
@@ -120,7 +133,7 @@ public class ReportUtilsTest extends BaseTest {
assertEquals(10, itemTrip.getMaxSpeed(), 0.01);
assertEquals(3000, itemTrip.getDistance(), 0.01);
- var stops = reportUtils.detectTripsAndStops(mock(Device.class) ,data, false, StopReportItem.class);
+ var stops = reportUtils.slowTripsAndStops(device, new Date(), new Date(), StopReportItem.class);
assertNotNull(stops);
assertFalse(stops.isEmpty());
@@ -136,8 +149,8 @@ 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());
}
@@ -151,17 +164,17 @@ 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),
- tripsConfig, mock(VelocityEngine.class), null);
+ mock(Config.class), storage, mock(PermissionsService.class), mock(VelocityEngine.class), null);
- var trips = reportUtils.detectTripsAndStops(mock(Device.class) ,data, false, TripReportItem.class);
+ var trips = reportUtils.slowTripsAndStops(device, new Date(), new Date(), TripReportItem.class);
assertNotNull(trips);
assertFalse(trips.isEmpty());
@@ -175,7 +188,7 @@ public class ReportUtilsTest extends BaseTest {
assertEquals(10, itemTrip.getMaxSpeed(), 0.01);
assertEquals(3000, itemTrip.getDistance(), 0.01);
- trips = reportUtils.detectTripsAndStops(mock(Device.class) ,data, false, TripReportItem.class);
+ trips = reportUtils.slowTripsAndStops(device, new Date(), new Date(), TripReportItem.class);
assertNotNull(trips);
assertFalse(trips.isEmpty());
@@ -189,7 +202,7 @@ public class ReportUtilsTest extends BaseTest {
assertEquals(10, itemTrip.getMaxSpeed(), 0.01);
assertEquals(3000, itemTrip.getDistance(), 0.01);
- var stops = reportUtils.detectTripsAndStops(mock(Device.class) ,data, false, StopReportItem.class);
+ var stops = reportUtils.slowTripsAndStops(device, new Date(), new Date(), StopReportItem.class);
assertNotNull(stops);
assertFalse(stops.isEmpty());
@@ -205,8 +218,8 @@ 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());
}
@@ -224,15 +237,15 @@ 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),
- tripsConfig, mock(VelocityEngine.class), null);
+ mock(Config.class), storage, mock(PermissionsService.class), mock(VelocityEngine.class), null);
- var trips = reportUtils.detectTripsAndStops(mock(Device.class) ,data, false, TripReportItem.class);
+ var trips = reportUtils.slowTripsAndStops(device, new Date(), new Date(), TripReportItem.class);
assertNotNull(trips);
assertFalse(trips.isEmpty());
@@ -246,7 +259,7 @@ public class ReportUtilsTest extends BaseTest {
assertEquals(10, itemTrip.getMaxSpeed(), 0.01);
assertEquals(7000, itemTrip.getDistance(), 0.01);
- var stops = reportUtils.detectTripsAndStops(mock(Device.class) ,data, false, StopReportItem.class);
+ var stops = reportUtils.slowTripsAndStops(device, new Date(), new Date(), StopReportItem.class);
assertNotNull(stops);
assertFalse(stops.isEmpty());
@@ -262,28 +275,28 @@ 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 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),
- tripsConfig, mock(VelocityEngine.class), null);
+ mock(Config.class), storage, mock(PermissionsService.class), mock(VelocityEngine.class), null);
- var result = reportUtils.detectTripsAndStops(mock(Device.class) ,data, false, StopReportItem.class);
+ var result = reportUtils.slowTripsAndStops(device, new Date(), new Date(), StopReportItem.class);
assertNotNull(result);
assertFalse(result.isEmpty());
@@ -299,20 +312,20 @@ public class ReportUtilsTest extends BaseTest {
@Test
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),
- tripsConfig, mock(VelocityEngine.class), null);
+ mock(Config.class), storage, mock(PermissionsService.class), mock(VelocityEngine.class), null);
- var result = reportUtils.detectTripsAndStops(mock(Device.class) ,data, false, StopReportItem.class);
+ var result = reportUtils.slowTripsAndStops(device, new Date(), new Date(), StopReportItem.class);
assertNotNull(result);
assertFalse(result.isEmpty());
@@ -328,20 +341,20 @@ public class ReportUtilsTest extends BaseTest {
@Test
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),
- tripsConfig, mock(VelocityEngine.class), null);
+ mock(Config.class), storage, mock(PermissionsService.class), mock(VelocityEngine.class), null);
- var result = reportUtils.detectTripsAndStops(mock(Device.class) ,data, false, StopReportItem.class);
+ var result = reportUtils.slowTripsAndStops(device, new Date(), new Date(), StopReportItem.class);
assertNotNull(result);
assertFalse(result.isEmpty());
@@ -349,28 +362,28 @@ public class ReportUtilsTest extends BaseTest {
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 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),
- tripsConfig, mock(VelocityEngine.class), null);
+ mock(Config.class), storage, mock(PermissionsService.class), mock(VelocityEngine.class), null);
- var result = reportUtils.detectTripsAndStops(mock(Device.class) ,data, false, StopReportItem.class);
+ var result = reportUtils.slowTripsAndStops(device, new Date(), new Date(), StopReportItem.class);
assertNotNull(result);
assertTrue(result.isEmpty());
@@ -380,7 +393,7 @@ public class ReportUtilsTest extends BaseTest {
@Test
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),
@@ -389,13 +402,13 @@ 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),
- tripsConfig, mock(VelocityEngine.class), null);
+ mock(Config.class), storage, mock(PermissionsService.class), mock(VelocityEngine.class), null);
- var trips = reportUtils.detectTripsAndStops(mock(Device.class) ,data, false, TripReportItem.class);
+ var trips = reportUtils.slowTripsAndStops(device, new Date(), new Date(), TripReportItem.class);
assertNotNull(trips);
assertFalse(trips.isEmpty());
@@ -409,7 +422,7 @@ public class ReportUtilsTest extends BaseTest {
assertEquals(7, itemTrip.getMaxSpeed(), 0.01);
assertEquals(600, itemTrip.getDistance(), 0.01);
- var stops = reportUtils.detectTripsAndStops(mock(Device.class) ,data, false, StopReportItem.class);
+ var stops = reportUtils.slowTripsAndStops(device, new Date(), new Date(), StopReportItem.class);
assertNotNull(stops);
assertFalse(stops.isEmpty());
diff --git a/src/test/java/org/traccar/speedlimit/OverpassSpeedLimitProviderTest.java b/src/test/java/org/traccar/speedlimit/OverpassSpeedLimitProviderTest.java
index 202983f1e..dbdf85420 100644
--- a/src/test/java/org/traccar/speedlimit/OverpassSpeedLimitProviderTest.java
+++ b/src/test/java/org/traccar/speedlimit/OverpassSpeedLimitProviderTest.java
@@ -1,22 +1,24 @@
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 javax.ws.rs.client.Client;
-import javax.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import org.traccar.config.Config;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
public class OverpassSpeedLimitProviderTest {
private final Client client = ClientBuilder.newClient();
- @Ignore
+ @Disabled
@Test
public void testOverpass() throws Exception {
- SpeedLimitProvider provider = new OverpassSpeedLimitProvider(client, "http://8.8.8.8/api/interpreter");
+ var config = new Config();
+ SpeedLimitProvider provider = new OverpassSpeedLimitProvider(config, 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;
diff --git a/swagger.json b/swagger.json
index 3eab9b522..b2209a20e 100644
--- a/swagger.json
+++ b/swagger.json
@@ -2,7 +2,7 @@
"openapi": "3.0.1",
"info": {
"title": "Traccar",
- "version": "5.5",
+ "version": "5.12",
"description": "Traccar GPS tracking server API documentation. To use the API you need to have a server instance. For testing purposes you can use one of free [demo servers](https://www.traccar.org/demo-server/). For production use you can install your own server or get a [subscription service](https://www.traccar.org/product/tracking-server/).",
"contact": {
"name": "Traccar Support",
@@ -764,15 +764,9 @@
"required": true
},
"responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/Permission"
- }
- }
- }
+ "204": {
+ "description": "No Content",
+ "content": {}
},
"400": {
"description": "No permission",
@@ -879,6 +873,54 @@
}
}
}
+ },
+ "delete": {
+ "summary": "Deletes all the Positions of a device in the time span specified",
+ "description": "",
+ "tags": [
+ "Positions"
+ ],
+ "parameters": [
+ {
+ "name": "deviceId",
+ "in": "query",
+ "description": "",
+ "schema": {
+ "type": "integer"
+ },
+ "required": true
+ },
+ {
+ "name": "from",
+ "in": "query",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "schema": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "required": true
+ },
+ {
+ "name": "to",
+ "in": "query",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "schema": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "required": true
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No Content",
+ "content": {}
+ },
+ "400": {
+ "description": "Bad Request",
+ "content": {}
+ }
+ }
}
},
"/server": {
@@ -1019,6 +1061,42 @@
}
}
},
+ "/session/openid/auth": {
+ "get": {
+ "summary": "Fetch Session information",
+ "tags": [
+ "Session"
+ ],
+ "parameters": [
+ {
+ }
+ ],
+ "responses": {
+ "303": {
+ "description": "Redirect to OpenID Connect identity provider",
+ "content": { }
+ }
+ }
+ }
+ },
+ "/session/openid/callback": {
+ "get": {
+ "summary": "OpenID Callback",
+ "tags": [
+ "Session"
+ ],
+ "parameters": [
+ {
+ }
+ ],
+ "responses": {
+ "303": {
+ "description": "Successful authentication, redirect to homepage",
+ "content": { }
+ }
+ }
+ }
+ },
"/users": {
"get": {
"summary": "Fetch a list of Users",
@@ -2647,6 +2725,12 @@
"type": "object",
"properties": {}
},
+ "geofenceIds": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
"attributes": {
"type": "object",
"properties": {}
@@ -2715,6 +2799,9 @@
"limitCommands": {
"type": "boolean"
},
+ "fixedEmail": {
+ "type": "boolean"
+ },
"poiLayer": {
"type": "string"
},
@@ -2775,6 +2862,12 @@
"coordinateFormat": {
"type": "string"
},
+ "openIdEnabled": {
+ "type": "boolean"
+ },
+ "openIdForce": {
+ "type": "boolean"
+ },
"attributes": {
"type": "object",
"properties": {}
@@ -2843,12 +2936,6 @@
"category": {
"type": "string"
},
- "geofenceIds": {
- "type": "array",
- "items": {
- "type": "integer"
- }
- },
"attributes": {
"type": "object",
"properties": {}
@@ -2878,39 +2965,43 @@
"properties": {
"userId": {
"type": "integer",
- "description": "User Id, can be only first parameter"
+ "description": "User id, can be only first parameter"
},
"deviceId": {
"type": "integer",
- "description": "Device Id, can be first parameter or second only in combination with userId"
+ "description": "Device id, can be first parameter or second only in combination with userId"
},
"groupId": {
"type": "integer",
- "description": "Group Id, can be first parameter or second only in combination with userId"
+ "description": "Group id, can be first parameter or second only in combination with userId"
},
"geofenceId": {
"type": "integer",
- "description": "Geofence Id, can be second parameter only"
+ "description": "Geofence id, can be second parameter only"
},
"notificationId": {
"type": "integer",
- "description": "Notification Id, can be second parameter only"
+ "description": "Notification id, can be second parameter only"
},
"calendarId": {
"type": "integer",
- "description": "Calendar Id, can be second parameter only and only in combination with userId"
+ "description": "Calendar id, can be second parameter only and only in combination with userId"
},
"attributeId": {
"type": "integer",
- "description": "Computed Attribute Id, can be second parameter only"
+ "description": "Computed attribute id, can be second parameter only"
},
"driverId": {
"type": "integer",
- "description": "Driver Id, can be second parameter only"
+ "description": "Driver id, can be second parameter only"
},
"managedUserId": {
"type": "integer",
- "description": "User Id, can be second parameter only and only in combination with userId"
+ "description": "User id, can be second parameter only and only in combination with userId"
+ },
+ "commandId": {
+ "type": "integer",
+ "description": "Saved command id, can be second parameter only"
}
},
"description": "This is a permission map that contain two object indexes. It is used to link/unlink objects. Order is important. Example: { deviceId:8, geofenceId: 16 }"
@@ -3487,4 +3578,4 @@
}
}
}
-} \ No newline at end of file
+}
diff --git a/templates/export/devices.xlsx b/templates/export/devices.xlsx
new file mode 100644
index 000000000..ab6d9d5be
--- /dev/null
+++ b/templates/export/devices.xlsx
Binary files differ
diff --git a/templates/full/alarm.vm b/templates/full/alarm.vm
index 9fae1f13b..fb596ecde 100644
--- a/templates/full/alarm.vm
+++ b/templates/full/alarm.vm
@@ -1,9 +1,87 @@
#set($subject = "$device.name: alarm!")
+#set($alarmName = $position.getString("alarm"))
+#if( $alarmName == "general")
+ #set($alarmName = "General")
+#elseif($alarmName == "sos")
+ #set($alarmName = "SOS")
+#elseif($alarmName == "vibration")
+ #set($alarmName = "Vibration")
+#elseif($alarmName == "movement")
+ #set($alarmName = "Movement")
+#elseif($alarmName == "lowspeed")
+ #set($alarmName = "Low Speed")
+#elseif($alarmName == "overspeed")
+ #set($alarmName = "Overspeed")
+#elseif($alarmName == "fallDown")
+ #set($alarmName = "Fall Down")
+#elseif($alarmName == "lowPower")
+ #set($alarmName = "Low Power")
+#elseif($alarmName == "lowBattery")
+ #set($alarmName = "Low Battery")
+#elseif($alarmName == "fault")
+ #set($alarmName = "Fault")
+#elseif($alarmName == "powerOff")
+ #set($alarmName = "Power Off")
+#elseif($alarmName == "powerOn")
+ #set($alarmName = "Power On")
+#elseif($alarmName == "door")
+ #set($alarmName = "Door")
+#elseif($alarmName == "lock")
+ #set($alarmName = "Lock")
+#elseif($alarmName == "unlock")
+ #set($alarmName = "Unlock")
+#elseif($alarmName == "geofence")
+ #set($alarmName = "Geofence")
+#elseif($alarmName == "geofenceEnter")
+ #set($alarmName = "Geofence Enter")
+#elseif($alarmName == "geofenceExit")
+ #set($alarmName = "Geofence Exit")
+#elseif($alarmName == "gpsAntennaCut")
+ #set($alarmName = "GPS Antenna Cut")
+#elseif($alarmName == "accident")
+ #set($alarmName = "Accident")
+#elseif($alarmName == "tow")
+ #set($alarmName = "Tow")
+#elseif($alarmName == "idle")
+ #set($alarmName = "Idle")
+#elseif($alarmName == "highRpm")
+ #set($alarmName = "High RPM")
+#elseif($alarmName == "hardAcceleration")
+ #set($alarmName = "Hard Acceleration")
+#elseif($alarmName == "hardBraking")
+ #set($alarmName = "Hard Braking")
+#elseif($alarmName == "hardCornering")
+ #set($alarmName = "Hard Cornering")
+#elseif($alarmName == "laneChange")
+ #set($alarmName = "Lane Change")
+#elseif($alarmName == "fatigueDriving")
+ #set($alarmName = "Fatigue Driving")
+#elseif($alarmName == "powerCut")
+ #set($alarmName = "Power Cut")
+#elseif($alarmName == "powerRestored")
+ #set($alarmName = "Power Restored")
+#elseif($alarmName == "jamming")
+ #set($alarmName = "Jamming")
+#elseif($alarmName == "temperature")
+ #set($alarmName = "Temperature")
+#elseif($alarmName == "parking")
+ #set($alarmName = "Parking")
+#elseif($alarmName == "bonnet")
+ #set($alarmName = "Bonnet")
+#elseif($alarmName == "footBrake")
+ #set($alarmName = "Foot Brake")
+#elseif($alarmName == "fuelLeak")
+ #set($alarmName = "Fuel Leak")
+#elseif($alarmName == "tampering")
+ #set($alarmName = "Tampering")
+#elseif($alarmName == "removing")
+ #set($alarmName = "Removing")
+#end
<!DOCTYPE html>
<html>
<body>
Device: $device.name<br>
-Alarm: $position.getString("alarm")<br>
+Alarm: $alarmName<br>
Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone)<br>
Point: <a href="$webUrl?eventId=$event.id">#{if}($position.address)$position.address#{else}$position.latitude&deg;, $position.longitude&deg;#{end}</a><br>
<br>
diff --git a/templates/full/deviceExpiration.vm b/templates/full/deviceExpiration.vm
new file mode 100644
index 000000000..879b31778
--- /dev/null
+++ b/templates/full/deviceExpiration.vm
@@ -0,0 +1,7 @@
+#set($subject = "Device expiration")
+<!DOCTYPE html>
+<html>
+<body>
+Your device $device.name has expired.
+</body>
+</html>
diff --git a/templates/full/deviceExpirationReminder.vm b/templates/full/deviceExpirationReminder.vm
new file mode 100644
index 000000000..aa47ac0ed
--- /dev/null
+++ b/templates/full/deviceExpirationReminder.vm
@@ -0,0 +1,7 @@
+#set($subject = "Device expiration reminder")
+<!DOCTYPE html>
+<html>
+<body>
+Your device $device.name will expire on $dateTool.format("YYYY-MM-dd", $expiration, $locale, $timezone).
+</body>
+</html>
diff --git a/templates/full/deviceOffline.vm b/templates/full/deviceOffline.vm
index 8f2c515b2..6d2122624 100644
--- a/templates/full/deviceOffline.vm
+++ b/templates/full/deviceOffline.vm
@@ -5,7 +5,6 @@
Device: $device.name<br>
Offline<br>
Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone)<br>
-Link: <a href="$webUrl?eventId=$event.id">$webUrl?eventId=$event.id</a><br>
<br>
<a href="$webUrl/settings/notifications?token=$token">Unsubscribe</a>
</body>
diff --git a/templates/full/deviceOnline.vm b/templates/full/deviceOnline.vm
index 81a4ccbc8..02260c4fb 100644
--- a/templates/full/deviceOnline.vm
+++ b/templates/full/deviceOnline.vm
@@ -5,7 +5,6 @@
Device: $device.name<br>
Online<br>
Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone)<br>
-Link: <a href="$webUrl?eventId=$event.id">$webUrl?eventId=$event.id</a><br>
<br>
<a href="$webUrl/settings/notifications?token=$token">Unsubscribe</a>
</body>
diff --git a/templates/full/deviceUnknown.vm b/templates/full/deviceUnknown.vm
index e012845e6..e99981069 100644
--- a/templates/full/deviceUnknown.vm
+++ b/templates/full/deviceUnknown.vm
@@ -5,7 +5,6 @@
Device: $device.name<br>
Status is unknown<br>
Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone)<br>
-Link: <a href="$webUrl?eventId=$event.id">$webUrl?eventId=$event.id</a><br>
<br>
<a href="$webUrl/settings/notifications?token=$token">Unsubscribe</a>
</body>
diff --git a/templates/full/queuedCommandSent.vm b/templates/full/queuedCommandSent.vm
new file mode 100644
index 000000000..148dd2094
--- /dev/null
+++ b/templates/full/queuedCommandSent.vm
@@ -0,0 +1,11 @@
+#set($subject = "$device.name: queued command sent")
+<!DOCTYPE html>
+<html>
+<body>
+Device: $device.name<br>
+Queued command sent<br>
+Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone)<br>
+<br>
+<a href="$webUrl/settings/notifications?token=$token">Unsubscribe</a>
+</body>
+</html>
diff --git a/templates/full/unknown.vm b/templates/full/unknown.vm
deleted file mode 100644
index 566ce0d42..000000000
--- a/templates/full/unknown.vm
+++ /dev/null
@@ -1,7 +0,0 @@
-#set($subject = "Unknown type")
-<!DOCTYPE html>
-<html>
-<body>
-Unknown type
-</body>
-</html>
diff --git a/templates/full/userExpiration.vm b/templates/full/userExpiration.vm
new file mode 100644
index 000000000..43fe2563e
--- /dev/null
+++ b/templates/full/userExpiration.vm
@@ -0,0 +1,7 @@
+#set($subject = "Account expiration")
+<!DOCTYPE html>
+<html>
+<body>
+Your user account has expired.
+</body>
+</html>
diff --git a/templates/full/userExpirationReminder.vm b/templates/full/userExpirationReminder.vm
new file mode 100644
index 000000000..ecdee0588
--- /dev/null
+++ b/templates/full/userExpirationReminder.vm
@@ -0,0 +1,7 @@
+#set($subject = "Account expiration reminder")
+<!DOCTYPE html>
+<html>
+<body>
+Your user account will expire on $dateTool.format("YYYY-MM-dd", $expiration, $locale, $timezone).
+</body>
+</html>
diff --git a/templates/short/alarm.vm b/templates/short/alarm.vm
index 15970dab8..effcb8f15 100644
--- a/templates/short/alarm.vm
+++ b/templates/short/alarm.vm
@@ -1,2 +1,80 @@
#set($subject = "$device.name: alarm!")
-$device.name alarm: $position.getString("alarm") at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone)
+#set($alarmName = $position.getString("alarm"))
+#if( $alarmName == "general")
+ #set($alarmName = "General")
+#elseif($alarmName == "sos")
+ #set($alarmName = "SOS")
+#elseif($alarmName == "vibration")
+ #set($alarmName = "Vibration")
+#elseif($alarmName == "movement")
+ #set($alarmName = "Movement")
+#elseif($alarmName == "lowspeed")
+ #set($alarmName = "Low Speed")
+#elseif($alarmName == "overspeed")
+ #set($alarmName = "Overspeed")
+#elseif($alarmName == "fallDown")
+ #set($alarmName = "Fall Down")
+#elseif($alarmName == "lowPower")
+ #set($alarmName = "Low Power")
+#elseif($alarmName == "lowBattery")
+ #set($alarmName = "Low Battery")
+#elseif($alarmName == "fault")
+ #set($alarmName = "Fault")
+#elseif($alarmName == "powerOff")
+ #set($alarmName = "Power Off")
+#elseif($alarmName == "powerOn")
+ #set($alarmName = "Power On")
+#elseif($alarmName == "door")
+ #set($alarmName = "Door")
+#elseif($alarmName == "lock")
+ #set($alarmName = "Lock")
+#elseif($alarmName == "unlock")
+ #set($alarmName = "Unlock")
+#elseif($alarmName == "geofence")
+ #set($alarmName = "Geofence")
+#elseif($alarmName == "geofenceEnter")
+ #set($alarmName = "Geofence Enter")
+#elseif($alarmName == "geofenceExit")
+ #set($alarmName = "Geofence Exit")
+#elseif($alarmName == "gpsAntennaCut")
+ #set($alarmName = "GPS Antenna Cut")
+#elseif($alarmName == "accident")
+ #set($alarmName = "Accident")
+#elseif($alarmName == "tow")
+ #set($alarmName = "Tow")
+#elseif($alarmName == "idle")
+ #set($alarmName = "Idle")
+#elseif($alarmName == "highRpm")
+ #set($alarmName = "High RPM")
+#elseif($alarmName == "hardAcceleration")
+ #set($alarmName = "Hard Acceleration")
+#elseif($alarmName == "hardBraking")
+ #set($alarmName = "Hard Braking")
+#elseif($alarmName == "hardCornering")
+ #set($alarmName = "Hard Cornering")
+#elseif($alarmName == "laneChange")
+ #set($alarmName = "Lane Change")
+#elseif($alarmName == "fatigueDriving")
+ #set($alarmName = "Fatigue Driving")
+#elseif($alarmName == "powerCut")
+ #set($alarmName = "Power Cut")
+#elseif($alarmName == "powerRestored")
+ #set($alarmName = "Power Restored")
+#elseif($alarmName == "jamming")
+ #set($alarmName = "Jamming")
+#elseif($alarmName == "temperature")
+ #set($alarmName = "Temperature")
+#elseif($alarmName == "parking")
+ #set($alarmName = "Parking")
+#elseif($alarmName == "bonnet")
+ #set($alarmName = "Bonnet")
+#elseif($alarmName == "footBrake")
+ #set($alarmName = "Foot Brake")
+#elseif($alarmName == "fuelLeak")
+ #set($alarmName = "Fuel Leak")
+#elseif($alarmName == "tampering")
+ #set($alarmName = "Tampering")
+#elseif($alarmName == "removing")
+ #set($alarmName = "Removing")
+#end
+$device.name alarm: $alarmName at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone)
diff --git a/templates/short/queuedCommandSent.vm b/templates/short/queuedCommandSent.vm
new file mode 100644
index 000000000..67f031280
--- /dev/null
+++ b/templates/short/queuedCommandSent.vm
@@ -0,0 +1,2 @@
+#set($subject = "$device.name: queued command sent")
+Queued command sent to $device.name at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone)
diff --git a/templates/short/unknown.vm b/templates/short/unknown.vm
deleted file mode 100644
index 2f9d5e3af..000000000
--- a/templates/short/unknown.vm
+++ /dev/null
@@ -1,2 +0,0 @@
-#set($subject = "Unknown type")
-Unknown type
diff --git a/tools/test-commands.py b/tools/test-commands.py
index 6e310051a..7efd963b4 100755
--- a/tools/test-commands.py
+++ b/tools/test-commands.py
@@ -6,9 +6,9 @@ import binascii
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("localhost", 5001))
#s.send(binascii.unhexlify('68680f0504035889905831401700df1a00000d0a'))
-s.send("imei:123456789012345,tracker,151030080103,,F,000101.000,A,5443.3834,N,02512.9071,E,0.00,0;")
+s.send(b"imei:123456789012345,tracker,151030080103,,F,000101.000,A,5443.3834,N,02512.9071,E,0.00,0;")
while True:
- print s.recv(1024)
+ print(s.recv(1024))
s.close()
diff --git a/tools/test-integration.py b/tools/test-integration.py
index 89f1c76ed..f31ad7a82 100755
--- a/tools/test-integration.py
+++ b/tools/test-integration.py
@@ -4,7 +4,7 @@ import sys
import os
import xml.etree.ElementTree
import urllib
-import urllib2
+import urllib.request as urllib2
import json
import socket
import time
@@ -18,10 +18,9 @@ messages = {
't55' : '$PGID,123456789012345*0F\r\n$GPRMC,120500.000,A,6000.0000,N,13000.0000,E,0.00,0.00,010112,,*33\r\n',
'xexun' : '111111120009,+436763737552,GPRMC,120600.000,A,6000.0000,N,13000.0000,E,0.00,0.00,010112,,,A*68,F,, imei:123456789012345,04,481.2,F:4.15V,0,139,2689,232,03,2725,0576\n',
'totem' : '$$B3123456789012345|AA$GPRMC,120700.000,A,6000.0000,N,13000.0000,E,0.00,,010112,,,A*74|01.8|01.0|01.5|000000000000|20120403234603|14251914|00000000|0012D888|0000|0.0000|3674|940B\r\n',
- 'meiligao' : '$$\x00\x60\x12\x34\x56\xFF\xFF\xFF\xFF\x99\x55120900.000,A,6000.0000,N,13000.0000,E,0.00,,010112,,*1C|11.5|194|0000|0000,0000\x69\x62\x0D\x0A',
'suntech' : 'SA200STT;123456;042;20120101;12:11:00;16d41;-15.618767;-056.083214;000.011;000.00;11;1;41557;12.21;000000;1;3205\r',
'h02' : '*HQ,123456789012345,V1,121300,A,6000.0000,N,13000.0000,E,0.00,0.00,010112,ffffffff,000000,000000,000000,000000#',
- 'jt600' : '$\x00\x00\x12\x34\x56\x11\x00\x1B\x01\x01\x12\x12\x14\x00\x60\x00\x00\x00\x13\x00\x00\x00\x0F\x00\x00\x07\x50\x00\x00\x00\x2B\x91\x04\x4D\x1F\xA1',
+ 'jt600' : '(1234567890,P45,290322,132412,25.28217,S,57.54683,W,A,0,0,5,0,0000000000,0,0,9,0)',
'v680' : '#123456789012345#1000#0#1000#AUT#1#66830FFB#13000.0000,E,6000.0000,N,001.41,259#010112#121600##',
'pt502' : '$POS,123456,121700.000,A,6000.0000,N,13000.0000,E,0.0,0.0,010112,,,A/00000,00000/0/23895000//\r\n',
'tr20' : '%%123456789012345,A,120101121800,N6000.0000E13000.0000,0,000,0,01034802,150,[Message]\r\n',
@@ -119,6 +118,15 @@ messages = {
'mobilogix': '[2020-10-25 20:45:09,T9,1,V1.2.3,123456789012,59,10.50,701,-25.236860,-45.708530,0,314]',
'swiftech': '@@123456789012345,,0,102040,1023.9670,N,07606.8160,E,2.26,151220,A,0127,1,1,03962,00000,#',
'ennfu': 'Ennfu:123456789012345,041504.00,A,3154.86654,N,11849.08737,E,0.053,,080121,20,3.72,21.4,V0.01$',
+ 'startek': '&&o125,123456789012345,000,0,,210702235150,A,27.263505,153.037061,11,1.2,0,0,31,5125,505|1|7032|8C89802,20,0000002D,00,00,01E2|019DF0\r\n',
+ 'hoopo': '{ "deviceId": "123456789012345", "assetName": "123456789012345", "assetType": "test", "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" }',
+ 'techtocruz': '$$A120,123456789012345,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',
+ 'flexapi': '${"topic":"v1/123456789012345/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\r\n',
+ 'jido': '*123456789012345,03,130517,160435,1820.5845,N,07833.2478,E,1,58#',
+ 'armoli': '[M123456789012345210122125205N38.735641E035.4727751E003340000000C00000E9E07FF:106AG505283H60E];',
+ 'teratrack': '{"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":"123456789012345","Index":"20","Slave":[]}',
+ 'envotech': '$80SLM,02,F,123456,130410155921,431750216,000040,0000,,00000000,\'13041015592110476673N10111459E001281*2A#',
+ 'bstpl': 'BSTPL$1,123456789012345,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#',
}
baseUrl = 'http://localhost:8082'
@@ -135,14 +143,14 @@ def load_ports():
if key.endswith('.port'):
ports[key[:-5]] = int(entry.text)
if debug:
- print '\nports: %s\n' % repr(ports)
+ print('\nports: {ports!r}\n')
return ports
def login():
request = urllib2.Request(baseUrl + '/api/session')
- response = urllib2.urlopen(request, urllib.parse.urlencode(user))
+ response = urllib2.urlopen(request, urllib.parse.urlencode(user).encode())
if debug:
- print '\nlogin: %s\n' % repr(json.load(response))
+ print(f'\nlogin: {json.load(response)!r}\n')
return response.headers.get('Set-Cookie')
def remove_devices(cookie):
@@ -151,7 +159,7 @@ def remove_devices(cookie):
response = urllib2.urlopen(request)
data = json.load(response)
if debug:
- print '\ndevices: %s\n' % repr(data)
+ print(f'\ndevices: {data!r}\n')
for device in data:
request = urllib2.Request(baseUrl + '/api/devices/' + str(device['id']))
request.add_header('Cookie', cookie)
@@ -163,15 +171,15 @@ def add_device(cookie, unique_id):
request.add_header('Cookie', cookie)
request.add_header('Content-Type', 'application/json')
device = { 'name' : unique_id, 'uniqueId' : unique_id }
- response = urllib2.urlopen(request, json.dumps(device))
+ response = urllib2.urlopen(request, json.dumps(device).encode())
data = json.load(response)
return data['id']
def send_message(port, message):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1', port))
- s.send(message)
- time.sleep(0.5)
+ s.send(message.encode('ascii'))
+ time.sleep(1.0)
s.close()
def get_protocols(cookie, device_id):
@@ -203,9 +211,9 @@ if __name__ == "__main__":
all = set(ports.keys())
protocols = set(messages.keys())
- print 'Total: %d' % len(all)
- print 'Missing: %d' % len(all - protocols)
- print 'Covered: %d' % len(protocols)
+ print(f'Total: {len(all)}')
+ print(f'Missing: {len(all - protocols)}')
+ print(f'Covered: {len(protocols)}')
for protocol in messages:
send_message(ports[protocol], messages[protocol])
@@ -213,8 +221,8 @@ if __name__ == "__main__":
for device in devices:
protocols -= set(get_protocols(cookie, devices[device]))
- print 'Success: %d' % (len(messages) - len(protocols))
- print 'Failed: %d' % len(protocols)
+ print(f'Success: {len(messages) - len(protocols)}')
+ print(f'Failed:{len(protocols)}')
if protocols:
- print '\nFailed: %s' % repr(list(protocols))
+ print(f'\nFailed: {list(protocols)!r}')
diff --git a/tools/test-map.py b/tools/test-map.py
index 362c95878..664917eff 100755
--- a/tools/test-map.py
+++ b/tools/test-map.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
import urllib
-import urllib2
+import urllib.request as urllib2
import http.client as httplib
import time
import random
@@ -14,7 +14,7 @@ devices = 500
def login():
request = urllib2.Request(baseUrl + '/api/session')
- response = urllib2.urlopen(request, urllib.parse.urlencode(user))
+ response = urllib2.urlopen(request, urllib.parse.urlencode(user).encode())
return response.headers.get('Set-Cookie')
def add_device(cookie, unique_id):
@@ -23,7 +23,7 @@ def add_device(cookie, unique_id):
request.add_header('Content-Type', 'application/json')
device = { 'name' : unique_id, 'uniqueId' : unique_id }
try:
- response = urllib2.urlopen(request, json.dumps(device))
+ response = urllib2.urlopen(request, json.dumps(device).encode())
except urllib2.HTTPError:
pass
diff --git a/traccar-web b/traccar-web
-Subproject 610ce1e5447d47c316f87887b64c4c459bad855
+Subproject 42db1a41cbf733ea4f82c86e5d45a6fbccc8b8f