aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/org/traccar/BasePipelineFactory.java45
-rw-r--r--src/main/java/org/traccar/BaseProtocol.java30
-rw-r--r--src/main/java/org/traccar/BaseProtocolDecoder.java177
-rw-r--r--src/main/java/org/traccar/BaseProtocolEncoder.java29
-rw-r--r--src/main/java/org/traccar/BaseProtocolPoller.java55
-rw-r--r--src/main/java/org/traccar/Context.java416
-rw-r--r--src/main/java/org/traccar/ExtendedObjectDecoder.java22
-rw-r--r--src/main/java/org/traccar/LifecycleObject.java21
-rw-r--r--src/main/java/org/traccar/Main.java55
-rw-r--r--src/main/java/org/traccar/MainEventHandler.java84
-rw-r--r--src/main/java/org/traccar/MainModule.java451
-rw-r--r--src/main/java/org/traccar/PositionForwardingHandler.java141
-rw-r--r--src/main/java/org/traccar/Protocol.java2
-rw-r--r--src/main/java/org/traccar/ServerManager.java97
-rw-r--r--src/main/java/org/traccar/TrackerClient.java125
-rw-r--r--src/main/java/org/traccar/TrackerConnector.java (renamed from src/main/java/org/traccar/database/ManagableObjects.java)15
-rw-r--r--src/main/java/org/traccar/TrackerServer.java75
-rw-r--r--src/main/java/org/traccar/WebDataHandler.java310
-rw-r--r--src/main/java/org/traccar/api/AsyncSocket.java34
-rw-r--r--src/main/java/org/traccar/api/AsyncSocketServlet.java35
-rw-r--r--src/main/java/org/traccar/api/BaseObjectResource.java185
-rw-r--r--src/main/java/org/traccar/api/BaseResource.java17
-rw-r--r--src/main/java/org/traccar/api/CorsResponseFilter.java16
-rw-r--r--src/main/java/org/traccar/api/ExtendedObjectResource.java57
-rw-r--r--src/main/java/org/traccar/api/MediaFilter.java53
-rw-r--r--src/main/java/org/traccar/api/SimpleObjectResource.java38
-rw-r--r--src/main/java/org/traccar/api/resource/AttributeResource.java69
-rw-r--r--src/main/java/org/traccar/api/resource/CommandResource.java122
-rw-r--r--src/main/java/org/traccar/api/resource/DeviceResource.java166
-rw-r--r--src/main/java/org/traccar/api/resource/EventResource.java28
-rw-r--r--src/main/java/org/traccar/api/resource/NotificationResource.java60
-rw-r--r--src/main/java/org/traccar/api/resource/PasswordResource.java69
-rw-r--r--src/main/java/org/traccar/api/resource/PermissionsResource.java87
-rw-r--r--src/main/java/org/traccar/api/resource/PositionResource.java108
-rw-r--r--src/main/java/org/traccar/api/resource/ReportResource.java286
-rw-r--r--src/main/java/org/traccar/api/resource/ServerResource.java66
-rw-r--r--src/main/java/org/traccar/api/resource/SessionResource.java97
-rw-r--r--src/main/java/org/traccar/api/resource/StatisticsResource.java18
-rw-r--r--src/main/java/org/traccar/api/resource/UserResource.java89
-rw-r--r--src/main/java/org/traccar/api/security/LoginService.java99
-rw-r--r--src/main/java/org/traccar/api/security/PermissionsService.java211
-rw-r--r--src/main/java/org/traccar/api/security/SecurityRequestFilter.java (renamed from src/main/java/org/traccar/api/SecurityRequestFilter.java)39
-rw-r--r--src/main/java/org/traccar/api/security/ServiceAccountUser.java (renamed from src/main/java/org/traccar/database/MaintenancesManager.java)19
-rw-r--r--src/main/java/org/traccar/api/security/UserPrincipal.java (renamed from src/main/java/org/traccar/api/UserPrincipal.java)2
-rw-r--r--src/main/java/org/traccar/api/security/UserSecurityContext.java (renamed from src/main/java/org/traccar/api/UserSecurityContext.java)6
-rw-r--r--src/main/java/org/traccar/api/signature/CryptoManager.java103
-rw-r--r--src/main/java/org/traccar/api/signature/KeystoreModel.java44
-rw-r--r--src/main/java/org/traccar/api/signature/TokenManager.java77
-rw-r--r--src/main/java/org/traccar/broadcast/BroadcastInterface.java45
-rw-r--r--src/main/java/org/traccar/broadcast/BroadcastMessage.java85
-rw-r--r--src/main/java/org/traccar/broadcast/BroadcastService.java23
-rw-r--r--src/main/java/org/traccar/broadcast/MulticastBroadcastService.java200
-rw-r--r--src/main/java/org/traccar/broadcast/NullBroadcastService.java36
-rw-r--r--src/main/java/org/traccar/config/Config.java45
-rw-r--r--src/main/java/org/traccar/config/ConfigKey.java73
-rw-r--r--src/main/java/org/traccar/config/ConfigSuffix.java81
-rw-r--r--src/main/java/org/traccar/config/KeyType.java2
-rw-r--r--src/main/java/org/traccar/config/Keys.java1123
-rw-r--r--src/main/java/org/traccar/database/AttributesManager.java36
-rw-r--r--src/main/java/org/traccar/database/BaseObjectManager.java178
-rw-r--r--src/main/java/org/traccar/database/CommandsManager.java221
-rw-r--r--src/main/java/org/traccar/database/ConnectionManager.java216
-rw-r--r--src/main/java/org/traccar/database/DataManager.java452
-rw-r--r--src/main/java/org/traccar/database/DeviceLookupService.java141
-rw-r--r--src/main/java/org/traccar/database/DeviceManager.java483
-rw-r--r--src/main/java/org/traccar/database/DriversManager.java100
-rw-r--r--src/main/java/org/traccar/database/ExtendedObjectManager.java143
-rw-r--r--src/main/java/org/traccar/database/GeofenceManager.java66
-rw-r--r--src/main/java/org/traccar/database/GroupsManager.java83
-rw-r--r--src/main/java/org/traccar/database/IdentityManager.java50
-rw-r--r--src/main/java/org/traccar/database/LdapProvider.java2
-rw-r--r--src/main/java/org/traccar/database/MailManager.java151
-rw-r--r--src/main/java/org/traccar/database/MediaManager.java19
-rw-r--r--src/main/java/org/traccar/database/NotificationManager.java199
-rw-r--r--src/main/java/org/traccar/database/PermissionsManager.java516
-rw-r--r--src/main/java/org/traccar/database/SimpleObjectManager.java100
-rw-r--r--src/main/java/org/traccar/database/StatisticsManager.java19
-rw-r--r--src/main/java/org/traccar/database/UsersManager.java84
-rw-r--r--src/main/java/org/traccar/forward/EventData.java78
-rw-r--r--src/main/java/org/traccar/forward/EventForwarder.java20
-rw-r--r--src/main/java/org/traccar/forward/EventForwarderJson.java68
-rw-r--r--src/main/java/org/traccar/forward/EventForwarderKafka.java58
-rw-r--r--src/main/java/org/traccar/forward/EventForwarderMqtt.java103
-rw-r--r--src/main/java/org/traccar/forward/PositionData.java45
-rw-r--r--src/main/java/org/traccar/forward/PositionForwarder.java20
-rw-r--r--src/main/java/org/traccar/forward/PositionForwarderJson.java86
-rw-r--r--src/main/java/org/traccar/forward/PositionForwarderKafka.java58
-rw-r--r--src/main/java/org/traccar/forward/PositionForwarderUrl.java166
-rw-r--r--src/main/java/org/traccar/forward/ResultHandler.java20
-rw-r--r--src/main/java/org/traccar/geocoder/BanGeocoder.java5
-rw-r--r--src/main/java/org/traccar/geocoder/BingMapsGeocoder.java5
-rw-r--r--src/main/java/org/traccar/geocoder/FactualGeocoder.java5
-rw-r--r--src/main/java/org/traccar/geocoder/GeoapifyGeocoder.java82
-rw-r--r--src/main/java/org/traccar/geocoder/GeocodeFarmGeocoder.java6
-rw-r--r--src/main/java/org/traccar/geocoder/GeocodeXyzGeocoder.java5
-rw-r--r--src/main/java/org/traccar/geocoder/Geocoder.java4
-rw-r--r--src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java5
-rw-r--r--src/main/java/org/traccar/geocoder/GoogleGeocoder.java5
-rw-r--r--src/main/java/org/traccar/geocoder/HereGeocoder.java6
-rw-r--r--src/main/java/org/traccar/geocoder/JsonGeocoder.java22
-rw-r--r--src/main/java/org/traccar/geocoder/LocationIqGeocoder.java29
-rw-r--r--src/main/java/org/traccar/geocoder/MapQuestGeocoder.java5
-rw-r--r--src/main/java/org/traccar/geocoder/MapTilerGeocoder.java5
-rw-r--r--src/main/java/org/traccar/geocoder/MapboxGeocoder.java5
-rw-r--r--src/main/java/org/traccar/geocoder/MapmyIndiaGeocoder.java5
-rw-r--r--src/main/java/org/traccar/geocoder/NominatimGeocoder.java6
-rw-r--r--src/main/java/org/traccar/geocoder/OpenCageGeocoder.java11
-rw-r--r--src/main/java/org/traccar/geocoder/PositionStackGeocoder.java5
-rw-r--r--src/main/java/org/traccar/geocoder/TestGeocoder.java36
-rw-r--r--src/main/java/org/traccar/geocoder/TomTomGeocoder.java5
-rw-r--r--src/main/java/org/traccar/geofence/GeofenceCircle.java4
-rw-r--r--src/main/java/org/traccar/geofence/GeofenceGeometry.java5
-rw-r--r--src/main/java/org/traccar/geofence/GeofencePolygon.java4
-rw-r--r--src/main/java/org/traccar/geofence/GeofencePolyline.java23
-rw-r--r--src/main/java/org/traccar/geolocation/GoogleGeolocationProvider.java8
-rw-r--r--src/main/java/org/traccar/geolocation/MozillaGeolocationProvider.java8
-rw-r--r--src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java12
-rw-r--r--src/main/java/org/traccar/geolocation/UniversalGeolocationProvider.java14
-rw-r--r--src/main/java/org/traccar/geolocation/UnwiredGeolocationProvider.java16
-rw-r--r--src/main/java/org/traccar/handler/ComputedAttributesHandler.java24
-rw-r--r--src/main/java/org/traccar/handler/CopyAttributesHandler.java36
-rw-r--r--src/main/java/org/traccar/handler/DefaultDataHandler.java19
-rw-r--r--src/main/java/org/traccar/handler/DistanceHandler.java20
-rw-r--r--src/main/java/org/traccar/handler/EngineHoursHandler.java19
-rw-r--r--src/main/java/org/traccar/handler/FilterHandler.java153
-rw-r--r--src/main/java/org/traccar/handler/GeocoderHandler.java22
-rw-r--r--src/main/java/org/traccar/handler/GeolocationHandler.java45
-rw-r--r--src/main/java/org/traccar/handler/HemisphereHandler.java9
-rw-r--r--src/main/java/org/traccar/handler/MotionHandler.java16
-rw-r--r--src/main/java/org/traccar/handler/OpenChannelHandler.java14
-rw-r--r--src/main/java/org/traccar/handler/RemoteAddressHandler.java26
-rw-r--r--src/main/java/org/traccar/handler/SpeedLimitHandler.java7
-rw-r--r--src/main/java/org/traccar/handler/StandardLoggingHandler.java8
-rw-r--r--src/main/java/org/traccar/handler/TimeHandler.java19
-rw-r--r--src/main/java/org/traccar/handler/events/AlertEventHandler.java17
-rw-r--r--src/main/java/org/traccar/handler/events/BaseEventHandler.java17
-rw-r--r--src/main/java/org/traccar/handler/events/BehaviorEventHandler.java16
-rw-r--r--src/main/java/org/traccar/handler/events/CommandResultEventHandler.java10
-rw-r--r--src/main/java/org/traccar/handler/events/DriverEventHandler.java25
-rw-r--r--src/main/java/org/traccar/handler/events/FuelDropEventHandler.java70
-rw-r--r--src/main/java/org/traccar/handler/events/FuelEventHandler.java79
-rw-r--r--src/main/java/org/traccar/handler/events/GeofenceEventHandler.java74
-rw-r--r--src/main/java/org/traccar/handler/events/IgnitionEventHandler.java26
-rw-r--r--src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java44
-rw-r--r--src/main/java/org/traccar/handler/events/MediaEventHandler.java49
-rw-r--r--src/main/java/org/traccar/handler/events/MotionEventHandler.java132
-rw-r--r--src/main/java/org/traccar/handler/events/OverspeedEventHandler.java135
-rw-r--r--src/main/java/org/traccar/helper/BitUtil.java4
-rw-r--r--src/main/java/org/traccar/helper/BufferUtil.java38
-rw-r--r--src/main/java/org/traccar/helper/Checksum.java15
-rw-r--r--src/main/java/org/traccar/helper/ClassScanner.java80
-rw-r--r--src/main/java/org/traccar/helper/Log.java18
-rw-r--r--src/main/java/org/traccar/helper/LogAction.java17
-rw-r--r--src/main/java/org/traccar/helper/NetworkUtil.java31
-rw-r--r--src/main/java/org/traccar/helper/ObjectMapperContextResolver.java (renamed from src/main/java/org/traccar/api/ObjectMapperProvider.java)22
-rw-r--r--src/main/java/org/traccar/helper/Parser.java18
-rw-r--r--src/main/java/org/traccar/helper/PatternUtil.java2
-rw-r--r--src/main/java/org/traccar/helper/model/AttributeUtil.java94
-rw-r--r--src/main/java/org/traccar/helper/model/DeviceUtil.java33
-rw-r--r--src/main/java/org/traccar/helper/model/GeofenceUtil.java42
-rw-r--r--src/main/java/org/traccar/helper/model/PositionUtil.java80
-rw-r--r--src/main/java/org/traccar/helper/model/UserUtil.java68
-rw-r--r--src/main/java/org/traccar/mail/LogMailManager.java44
-rw-r--r--src/main/java/org/traccar/mail/MailManager.java31
-rw-r--r--src/main/java/org/traccar/mail/SmtpMailManager.java160
-rw-r--r--src/main/java/org/traccar/model/Attribute.java3
-rw-r--r--src/main/java/org/traccar/model/BaseCommand.java (renamed from src/main/java/org/traccar/database/CalendarManager.java)17
-rw-r--r--src/main/java/org/traccar/model/BaseModel.java6
-rw-r--r--src/main/java/org/traccar/model/Calendar.java12
-rw-r--r--src/main/java/org/traccar/model/CellTower.java38
-rw-r--r--src/main/java/org/traccar/model/Command.java30
-rw-r--r--src/main/java/org/traccar/model/Device.java140
-rw-r--r--src/main/java/org/traccar/model/DeviceState.java71
-rw-r--r--src/main/java/org/traccar/model/Disableable.java39
-rw-r--r--src/main/java/org/traccar/model/Driver.java4
-rw-r--r--src/main/java/org/traccar/model/Event.java7
-rw-r--r--src/main/java/org/traccar/model/ExtendedModel.java39
-rw-r--r--src/main/java/org/traccar/model/Geofence.java22
-rw-r--r--src/main/java/org/traccar/model/Group.java3
-rw-r--r--src/main/java/org/traccar/model/Maintenance.java3
-rw-r--r--src/main/java/org/traccar/model/Network.java26
-rw-r--r--src/main/java/org/traccar/model/Notification.java6
-rw-r--r--src/main/java/org/traccar/model/Order.java3
-rw-r--r--src/main/java/org/traccar/model/Pair.java43
-rw-r--r--src/main/java/org/traccar/model/Permission.java109
-rw-r--r--src/main/java/org/traccar/model/Position.java26
-rw-r--r--src/main/java/org/traccar/model/QueuedCommand.java46
-rw-r--r--src/main/java/org/traccar/model/Server.java89
-rw-r--r--src/main/java/org/traccar/model/Statistics.java3
-rw-r--r--src/main/java/org/traccar/model/User.java68
-rw-r--r--src/main/java/org/traccar/model/UserRestrictions.java24
-rw-r--r--src/main/java/org/traccar/model/WifiAccessPoint.java23
-rw-r--r--src/main/java/org/traccar/notification/EventForwarder.java106
-rw-r--r--src/main/java/org/traccar/notification/NotificationFormatter.java51
-rw-r--r--src/main/java/org/traccar/notification/NotificatorManager.java102
-rw-r--r--src/main/java/org/traccar/notification/PropertiesProvider.java28
-rw-r--r--src/main/java/org/traccar/notification/TextTemplateFormatter.java43
-rw-r--r--src/main/java/org/traccar/notificators/Notificator.java22
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorFirebase.java133
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorMail.java26
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorNull.java12
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorPushover.java72
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorSms.java41
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorTelegram.java63
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorTraccar.java68
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorWeb.java40
-rw-r--r--src/main/java/org/traccar/protocol/AdmProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/AdmProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/AisProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/AisProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/AlematicsProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/AlematicsProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/AnytrekProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/AnytrekProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/ApelProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/ApelProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/AplicomProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/AplicomProtocolDecoder.java5
-rw-r--r--src/main/java/org/traccar/protocol/AppelloProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/AppelloProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/AquilaProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/AquilaProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Ardi01Protocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/Ardi01ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/ArknavProtocol.java21
-rw-r--r--src/main/java/org/traccar/protocol/ArknavProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/ArknavX8Protocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/ArknavX8ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/ArmoliProtocol.java44
-rw-r--r--src/main/java/org/traccar/protocol/ArmoliProtocolDecoder.java151
-rw-r--r--src/main/java/org/traccar/protocol/ArmoliProtocolPoller.java (renamed from src/main/java/org/traccar/DeviceSession.java)31
-rw-r--r--src/main/java/org/traccar/protocol/ArnaviBinaryProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/ArnaviProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java10
-rw-r--r--src/main/java/org/traccar/protocol/ArnaviTextProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/AstraProtocol.java14
-rw-r--r--src/main/java/org/traccar/protocol/AstraProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/At2000Protocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/At2000ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/AtrackProtocol.java14
-rw-r--r--src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java40
-rw-r--r--src/main/java/org/traccar/protocol/AuroProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/AuroProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/AustinNbProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/AustinNbProtocolDecoder.java7
-rw-r--r--src/main/java/org/traccar/protocol/AutoFonProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/AutoFonProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/AutoGradeProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/AutoGradeProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/AutoTrackProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/AutoTrackProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/AvemaProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/AvemaProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Avl301Protocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/Avl301ProtocolDecoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/B2316Protocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/B2316ProtocolDecoder.java5
-rw-r--r--src/main/java/org/traccar/protocol/BceProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/BceProtocolDecoder.java14
-rw-r--r--src/main/java/org/traccar/protocol/BlackKiteProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/BlackKiteProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/BlueProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/BlueProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/BoxProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/BoxProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/BstplProtocol.java43
-rw-r--r--src/main/java/org/traccar/protocol/BstplProtocolDecoder.java137
-rw-r--r--src/main/java/org/traccar/protocol/C2stekProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/C2stekProtocolDecoder.java20
-rw-r--r--src/main/java/org/traccar/protocol/CalAmpProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/CalAmpProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/CarTrackProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/CarTrackProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/CarcellProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/CarcellProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/CarscopProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/CarscopProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/CastelProtocol.java14
-rw-r--r--src/main/java/org/traccar/protocol/CastelProtocolDecoder.java9
-rw-r--r--src/main/java/org/traccar/protocol/CastelProtocolEncoder.java7
-rw-r--r--src/main/java/org/traccar/protocol/CautelaProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/CautelaProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/CellocatorProtocol.java14
-rw-r--r--src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java12
-rw-r--r--src/main/java/org/traccar/protocol/CguardProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/CguardProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/CityeasyProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/CityeasyProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/ContinentalProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/ContinentalProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/CradlepointProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/CradlepointProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/DingtekProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/DingtekProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/DishaProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/DishaProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/DmtHttpProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/DmtHttpProtocolDecoder.java32
-rw-r--r--src/main/java/org/traccar/protocol/DmtProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/DmtProtocolDecoder.java6
-rw-r--r--src/main/java/org/traccar/protocol/DolphinProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/DolphinProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Dsf22FrameDecoder.java44
-rw-r--r--src/main/java/org/traccar/protocol/Dsf22Protocol.java44
-rw-r--r--src/main/java/org/traccar/protocol/Dsf22ProtocolDecoder.java89
-rw-r--r--src/main/java/org/traccar/protocol/DualcamProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/DualcamProtocolDecoder.java41
-rw-r--r--src/main/java/org/traccar/protocol/DwayProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/DwayProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/EasyTrackProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/EelinkProtocol.java14
-rw-r--r--src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java58
-rw-r--r--src/main/java/org/traccar/protocol/EgtsProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/EgtsProtocolDecoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/EnforaProtocol.java14
-rw-r--r--src/main/java/org/traccar/protocol/EnforaProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/EnnfuProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/EnnfuProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/EnvotechProtocol.java43
-rw-r--r--src/main/java/org/traccar/protocol/EnvotechProtocolDecoder.java116
-rw-r--r--src/main/java/org/traccar/protocol/EsealProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/EsealProtocolDecoder.java11
-rw-r--r--src/main/java/org/traccar/protocol/EskyProtocol.java14
-rw-r--r--src/main/java/org/traccar/protocol/EskyProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/ExtremTracProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/ExtremTracProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/FifotrackProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java31
-rw-r--r--src/main/java/org/traccar/protocol/FlespiProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/FlespiProtocolDecoder.java19
-rw-r--r--src/main/java/org/traccar/protocol/FlexApiProtocol.java43
-rw-r--r--src/main/java/org/traccar/protocol/FlexApiProtocolDecoder.java173
-rw-r--r--src/main/java/org/traccar/protocol/FlexCommProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/FlexCommProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/FlexibleReportProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/FlexibleReportProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/FlextrackProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/FlextrackProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/FoxProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/FoxProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/FreedomProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/FreedomProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/FreematicsProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/FreematicsProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/FutureWayProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/FutureWayProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/G1rusFrameDecoder.java49
-rw-r--r--src/main/java/org/traccar/protocol/G1rusProtocol.java40
-rw-r--r--src/main/java/org/traccar/protocol/G1rusProtocolDecoder.java165
-rw-r--r--src/main/java/org/traccar/protocol/GalileoProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java76
-rw-r--r--src/main/java/org/traccar/protocol/GatorProtocol.java14
-rw-r--r--src/main/java/org/traccar/protocol/GatorProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/GenxProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/GenxProtocolDecoder.java9
-rw-r--r--src/main/java/org/traccar/protocol/Gl100Protocol.java14
-rw-r--r--src/main/java/org/traccar/protocol/Gl100ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Gl200BinaryProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Gl200Protocol.java15
-rw-r--r--src/main/java/org/traccar/protocol/Gl200ProtocolDecoder.java10
-rw-r--r--src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java84
-rw-r--r--src/main/java/org/traccar/protocol/GlobalSatProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/GlobalSatProtocolDecoder.java10
-rw-r--r--src/main/java/org/traccar/protocol/GlobalstarProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java14
-rw-r--r--src/main/java/org/traccar/protocol/GnxProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/GnxProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/GoSafeProtocol.java14
-rw-r--r--src/main/java/org/traccar/protocol/GoSafeProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/GotopProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/GotopProtocolDecoder.java21
-rw-r--r--src/main/java/org/traccar/protocol/Gps056Protocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/Gps056ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Gps103Protocol.java14
-rw-r--r--src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java25
-rw-r--r--src/main/java/org/traccar/protocol/Gps103ProtocolEncoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/GpsGateProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/GpsGateProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/GpsMarkerProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/GpsMarkerProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/GpsmtaProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/GpsmtaProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/GranitProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/GranitProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Gs100Protocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/Gs100ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Gt02Protocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/Gt02ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Gt06Protocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java986
-rw-r--r--src/main/java/org/traccar/protocol/Gt06ProtocolEncoder.java42
-rw-r--r--src/main/java/org/traccar/protocol/Gt30Protocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/Gt30ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/H02Protocol.java17
-rw-r--r--src/main/java/org/traccar/protocol/H02ProtocolDecoder.java42
-rw-r--r--src/main/java/org/traccar/protocol/H02ProtocolEncoder.java12
-rw-r--r--src/main/java/org/traccar/protocol/HaicomProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/HaicomProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/HomtecsProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/HomtecsProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/HoopoProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/HoopoProtocolDecoder.java10
-rw-r--r--src/main/java/org/traccar/protocol/HuaShengProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/HuabaoProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java445
-rw-r--r--src/main/java/org/traccar/protocol/HuabaoProtocolEncoder.java9
-rw-r--r--src/main/java/org/traccar/protocol/HunterProProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/HunterProProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/IdplProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/IdplProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/IntellitracProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/IntellitracProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/IotmProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/IotmProtocolDecoder.java8
-rw-r--r--src/main/java/org/traccar/protocol/ItsProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/ItsProtocolDecoder.java5
-rw-r--r--src/main/java/org/traccar/protocol/Ivt401Protocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/Ivt401ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/JidoProtocol.java43
-rw-r--r--src/main/java/org/traccar/protocol/JidoProtocolDecoder.java128
-rw-r--r--src/main/java/org/traccar/protocol/JpKorjarProtocol.java12
-rw-r--r--src/main/java/org/traccar/protocol/JpKorjarProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Jt600Protocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java88
-rw-r--r--src/main/java/org/traccar/protocol/KenjiProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/KenjiProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/KhdProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/KhdProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/KhdProtocolEncoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/L100Protocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/L100ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/LacakProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/LacakProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/LaipacProtocol.java12
-rw-r--r--src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java8
-rw-r--r--src/main/java/org/traccar/protocol/LeafSpyProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/LeafSpyProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/M2cProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/M2cProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/M2mProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/M2mProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/MaestroProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/MaestroProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/ManPowerProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/ManPowerProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Mavlink2Protocol.java11
-rw-r--r--src/main/java/org/traccar/protocol/Mavlink2ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/MegastekProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/MegastekProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/MeiligaoProtocol.java15
-rw-r--r--src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java8
-rw-r--r--src/main/java/org/traccar/protocol/MeiligaoProtocolEncoder.java54
-rw-r--r--src/main/java/org/traccar/protocol/MeitrackProtocol.java14
-rw-r--r--src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java97
-rw-r--r--src/main/java/org/traccar/protocol/MeitrackProtocolEncoder.java9
-rw-r--r--src/main/java/org/traccar/protocol/MictrackProtocol.java14
-rw-r--r--src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java27
-rw-r--r--src/main/java/org/traccar/protocol/MilesmateProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/MilesmateProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/MiniFinderProtocol.java14
-rw-r--r--src/main/java/org/traccar/protocol/MiniFinderProtocolDecoder.java19
-rw-r--r--src/main/java/org/traccar/protocol/Minifinder2Protocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java28
-rw-r--r--src/main/java/org/traccar/protocol/MobilogixProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/MobilogixProtocolDecoder.java12
-rw-r--r--src/main/java/org/traccar/protocol/MoovboxProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/MoovboxProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/MotorProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/MotorProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Mta6Protocol.java13
-rw-r--r--src/main/java/org/traccar/protocol/Mta6ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/MtxProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/MtxProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/MxtProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/MxtProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/NavigilProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/NavigilProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/NavisProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/NavisProtocolDecoder.java11
-rw-r--r--src/main/java/org/traccar/protocol/NavisetProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/NavisetProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/NavtelecomFrameDecoder.java9
-rw-r--r--src/main/java/org/traccar/protocol/NavtelecomProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java93
-rw-r--r--src/main/java/org/traccar/protocol/NdtpV6FrameDecoder.java32
-rw-r--r--src/main/java/org/traccar/protocol/NdtpV6Protocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/NdtpV6ProtocolDecoder.java203
-rw-r--r--src/main/java/org/traccar/protocol/NdtpV6ProtocolEncoder.java42
-rw-r--r--src/main/java/org/traccar/protocol/NeosProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/NeosProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/NetProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/NetProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/NiotProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/NiotProtocolDecoder.java13
-rw-r--r--src/main/java/org/traccar/protocol/NoranProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/NoranProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/NvsProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/NvsProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/NyitechProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/NyitechProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/ObdDongleProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/ObdDongleProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/OigoProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/OigoProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/OkoProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/OkoProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/OmnicommProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/OmnicommProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/OpenGtsProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/OpenGtsProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/OrbcommProtocol.java44
-rw-r--r--src/main/java/org/traccar/protocol/OrbcommProtocolDecoder.java124
-rw-r--r--src/main/java/org/traccar/protocol/OrbcommProtocolPoller.java74
-rw-r--r--src/main/java/org/traccar/protocol/OrionProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/OrionProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/OsmAndProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/OsmAndProtocolDecoder.java31
-rw-r--r--src/main/java/org/traccar/protocol/OutsafeProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/OutsafeProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/OwnTracksProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/PacificTrackProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/PathAwayProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/PathAwayProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/PiligrimProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/PiligrimProtocolDecoder.java58
-rw-r--r--src/main/java/org/traccar/protocol/PluginProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/PluginProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/PolteProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/PolteProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/PortmanProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/PortmanProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/PretraceProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/PretraceProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/PretraceProtocolEncoder.java5
-rw-r--r--src/main/java/org/traccar/protocol/PricolProtocol.java14
-rw-r--r--src/main/java/org/traccar/protocol/PricolProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/ProgressProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/ProgressProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/PstProtocol.java14
-rw-r--r--src/main/java/org/traccar/protocol/PstProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Pt215Protocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/Pt215ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Pt3000Protocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/Pt3000ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Pt502Protocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/Pt502ProtocolDecoder.java6
-rw-r--r--src/main/java/org/traccar/protocol/Pt60Protocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/Pt60ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/R12wProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/R12wProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/RaceDynamicsProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/RaceDynamicsProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/RadarProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/RadarProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/RaveonProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/RaveonProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/RecodaProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/RecodaProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/RetranslatorProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/RetranslatorProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/RfTrackProtocol.java43
-rw-r--r--src/main/java/org/traccar/protocol/RfTrackProtocolDecoder.java163
-rw-r--r--src/main/java/org/traccar/protocol/RitiProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/RitiProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/RoboTrackProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/RoboTrackProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/RstProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/RstProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/RuptelaProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java39
-rw-r--r--src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/S168Protocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/S168ProtocolDecoder.java46
-rw-r--r--src/main/java/org/traccar/protocol/SabertekProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/SabertekProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/SanavProtocol.java14
-rw-r--r--src/main/java/org/traccar/protocol/SanavProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/SanulProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/SanulProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/SatsolProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/SatsolProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/SigfoxProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java17
-rw-r--r--src/main/java/org/traccar/protocol/SiwiProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/SiwiProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/SkypatrolProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/SkypatrolProtocolDecoder.java11
-rw-r--r--src/main/java/org/traccar/protocol/SmartSoleProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/SmartSoleProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/SmokeyProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/SmokeyProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/SolarPoweredProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/SolarPoweredProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/SpotProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/SpotProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/StarLinkProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java26
-rw-r--r--src/main/java/org/traccar/protocol/StarcomProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/StarcomProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/StartekProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/StartekProtocolDecoder.java49
-rw-r--r--src/main/java/org/traccar/protocol/StbProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/StbProtocolDecoder.java33
-rw-r--r--src/main/java/org/traccar/protocol/Stl060Protocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/Stl060ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/SuntechProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java173
-rw-r--r--src/main/java/org/traccar/protocol/SuntechProtocolEncoder.java92
-rw-r--r--src/main/java/org/traccar/protocol/SupermateProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/SupermateProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/SviasProtocol.java11
-rw-r--r--src/main/java/org/traccar/protocol/SviasProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/SwiftechProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/SwiftechProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/T55Protocol.java14
-rw-r--r--src/main/java/org/traccar/protocol/T55ProtocolDecoder.java83
-rw-r--r--src/main/java/org/traccar/protocol/T57Protocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/T57ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/T800xProtocol.java19
-rw-r--r--src/main/java/org/traccar/protocol/T800xProtocolDecoder.java42
-rw-r--r--src/main/java/org/traccar/protocol/TaipPrefixEncoder.java15
-rw-r--r--src/main/java/org/traccar/protocol/TaipProtocol.java14
-rw-r--r--src/main/java/org/traccar/protocol/TaipProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/TechTltProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/TechTltProtocolDecoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/TechtoCruzFrameDecoder.java44
-rw-r--r--src/main/java/org/traccar/protocol/TechtoCruzProtocol.java13
-rw-r--r--src/main/java/org/traccar/protocol/TechtoCruzProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/TekProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/TekProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/TelemaxProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/TelemaxProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/TelicProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/TelicProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/TeltonikaFrameDecoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/TeltonikaProtocol.java14
-rw-r--r--src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java325
-rw-r--r--src/main/java/org/traccar/protocol/TeraTrackProtocol.java42
-rw-r--r--src/main/java/org/traccar/protocol/TeraTrackProtocolDecoder.java79
-rw-r--r--src/main/java/org/traccar/protocol/ThinkPowerProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/ThinkPowerProtocolDecoder.java9
-rw-r--r--src/main/java/org/traccar/protocol/ThinkRaceProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/ThinkRaceProtocolDecoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/ThurayaProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/ThurayaProtocolDecoder.java195
-rw-r--r--src/main/java/org/traccar/protocol/Tk102Protocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/Tk102ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Tk103Protocol.java14
-rw-r--r--src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java60
-rw-r--r--src/main/java/org/traccar/protocol/Tk103ProtocolEncoder.java11
-rw-r--r--src/main/java/org/traccar/protocol/Tlt2hProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java31
-rw-r--r--src/main/java/org/traccar/protocol/TlvProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/TlvProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/TmgProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/TmgProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/TopflytechProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/TopflytechProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/TopinProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/TopinProtocolDecoder.java37
-rw-r--r--src/main/java/org/traccar/protocol/TotemProtocol.java27
-rw-r--r--src/main/java/org/traccar/protocol/TotemProtocolDecoder.java22
-rw-r--r--src/main/java/org/traccar/protocol/TotemProtocolEncoder.java38
-rw-r--r--src/main/java/org/traccar/protocol/TotemProtocolSmsEncoder.java37
-rw-r--r--src/main/java/org/traccar/protocol/Tr20Protocol.java14
-rw-r--r--src/main/java/org/traccar/protocol/Tr20ProtocolDecoder.java6
-rw-r--r--src/main/java/org/traccar/protocol/Tr900Protocol.java14
-rw-r--r--src/main/java/org/traccar/protocol/Tr900ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/TrackboxProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/TrackboxProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/TrakMateProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/TrakMateProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/TramigoProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/TramigoProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/TrvProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/TrvProtocolDecoder.java6
-rw-r--r--src/main/java/org/traccar/protocol/Tt8850Protocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/Tt8850ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/TytanProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/TytanProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/TzoneProtocol.java11
-rw-r--r--src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java30
-rw-r--r--src/main/java/org/traccar/protocol/UlbotechProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/UlbotechProtocolDecoder.java10
-rw-r--r--src/main/java/org/traccar/protocol/UproProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/UproProtocolDecoder.java5
-rw-r--r--src/main/java/org/traccar/protocol/UuxProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/UuxProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/V680Protocol.java14
-rw-r--r--src/main/java/org/traccar/protocol/V680ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/VisiontekProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/VisiontekProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/VnetProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/VnetProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Vt200Protocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/Vt200ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/VtfmsProtocol.java15
-rw-r--r--src/main/java/org/traccar/protocol/VtfmsProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/WatchProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/WatchProtocolDecoder.java81
-rw-r--r--src/main/java/org/traccar/protocol/WatchProtocolEncoder.java3
-rw-r--r--src/main/java/org/traccar/protocol/WialonProtocol.java26
-rw-r--r--src/main/java/org/traccar/protocol/WialonProtocolDecoder.java18
-rw-r--r--src/main/java/org/traccar/protocol/WliProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/WliProtocolDecoder.java76
-rw-r--r--src/main/java/org/traccar/protocol/WondexProtocol.java15
-rw-r--r--src/main/java/org/traccar/protocol/WondexProtocolDecoder.java11
-rw-r--r--src/main/java/org/traccar/protocol/WondexProtocolEncoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/WristbandProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/WristbandProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Xexun2FrameEncoder.java48
-rw-r--r--src/main/java/org/traccar/protocol/Xexun2Protocol.java18
-rw-r--r--src/main/java/org/traccar/protocol/Xexun2ProtocolDecoder.java46
-rw-r--r--src/main/java/org/traccar/protocol/Xexun2ProtocolEncoder.java70
-rw-r--r--src/main/java/org/traccar/protocol/XexunProtocol.java18
-rw-r--r--src/main/java/org/traccar/protocol/XexunProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/XirgoProtocol.java17
-rw-r--r--src/main/java/org/traccar/protocol/XirgoProtocolDecoder.java9
-rw-r--r--src/main/java/org/traccar/protocol/Xrb28Protocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Xt013Protocol.java15
-rw-r--r--src/main/java/org/traccar/protocol/Xt013ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Xt2400Protocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java8
-rw-r--r--src/main/java/org/traccar/protocol/YwtProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/YwtProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/reports/CsvExportProvider.java76
-rw-r--r--src/main/java/org/traccar/reports/Events.java133
-rw-r--r--src/main/java/org/traccar/reports/EventsReportProvider.java169
-rw-r--r--src/main/java/org/traccar/reports/GpxExportProvider.java76
-rw-r--r--src/main/java/org/traccar/reports/KmlExportProvider.java80
-rw-r--r--src/main/java/org/traccar/reports/ReportUtils.java368
-rw-r--r--src/main/java/org/traccar/reports/Route.java85
-rw-r--r--src/main/java/org/traccar/reports/RouteReportProvider.java102
-rw-r--r--src/main/java/org/traccar/reports/Stops.java102
-rw-r--r--src/main/java/org/traccar/reports/StopsReportProvider.java110
-rw-r--r--src/main/java/org/traccar/reports/SummaryReportProvider.java (renamed from src/main/java/org/traccar/reports/Summary.java)120
-rw-r--r--src/main/java/org/traccar/reports/Trips.java100
-rw-r--r--src/main/java/org/traccar/reports/TripsReportProvider.java110
-rw-r--r--src/main/java/org/traccar/reports/common/ReportUtils.java399
-rw-r--r--src/main/java/org/traccar/reports/common/TripsConfig.java (renamed from src/main/java/org/traccar/reports/model/TripsConfig.java)67
-rw-r--r--src/main/java/org/traccar/reports/model/BaseReportItem.java (renamed from src/main/java/org/traccar/reports/model/BaseReport.java)2
-rw-r--r--src/main/java/org/traccar/reports/model/DeviceReportSection.java (renamed from src/main/java/org/traccar/reports/model/DeviceReport.java)2
-rw-r--r--src/main/java/org/traccar/reports/model/StopReportItem.java (renamed from src/main/java/org/traccar/reports/model/StopReport.java)2
-rw-r--r--src/main/java/org/traccar/reports/model/SummaryReportItem.java (renamed from src/main/java/org/traccar/reports/model/SummaryReport.java)2
-rw-r--r--src/main/java/org/traccar/reports/model/TripReportItem.java (renamed from src/main/java/org/traccar/reports/model/TripReport.java)2
-rw-r--r--src/main/java/org/traccar/schedule/ScheduleManager.java28
-rw-r--r--src/main/java/org/traccar/schedule/ScheduleTask.java22
-rw-r--r--src/main/java/org/traccar/schedule/TaskDeviceInactivityCheck.java42
-rw-r--r--src/main/java/org/traccar/schedule/TaskHealthCheck.java (renamed from src/main/java/org/traccar/api/HealthCheckService.java)70
-rw-r--r--src/main/java/org/traccar/schedule/TaskWebSocketKeepalive.java17
-rw-r--r--src/main/java/org/traccar/session/ConnectionManager.java374
-rw-r--r--src/main/java/org/traccar/session/DeviceSession.java (renamed from src/main/java/org/traccar/database/ActiveDevice.java)50
-rw-r--r--src/main/java/org/traccar/session/Endpoint.java58
-rw-r--r--src/main/java/org/traccar/session/cache/CacheKey.java57
-rw-r--r--src/main/java/org/traccar/session/cache/CacheManager.java425
-rw-r--r--src/main/java/org/traccar/session/cache/CacheValue.java53
-rw-r--r--src/main/java/org/traccar/session/state/MotionProcessor.java81
-rw-r--r--src/main/java/org/traccar/session/state/MotionState.java101
-rw-r--r--src/main/java/org/traccar/session/state/OverspeedProcessor.java65
-rw-r--r--src/main/java/org/traccar/session/state/OverspeedState.java88
-rw-r--r--src/main/java/org/traccar/sms/HttpSmsClient.java55
-rw-r--r--src/main/java/org/traccar/sms/SmsManager.java7
-rw-r--r--src/main/java/org/traccar/sms/SnsSmsClient.java33
-rw-r--r--src/main/java/org/traccar/speedlimit/OverpassSpeedLimitProvider.java10
-rw-r--r--src/main/java/org/traccar/storage/DatabaseModule.java103
-rw-r--r--src/main/java/org/traccar/storage/DatabaseStorage.java410
-rw-r--r--src/main/java/org/traccar/storage/MemoryStorage.java79
-rw-r--r--src/main/java/org/traccar/storage/QueryBuilder.java (renamed from src/main/java/org/traccar/database/QueryBuilder.java)124
-rw-r--r--src/main/java/org/traccar/storage/QueryIgnore.java (renamed from src/main/java/org/traccar/database/QueryIgnore.java)2
-rw-r--r--src/main/java/org/traccar/storage/Storage.java53
-rw-r--r--src/main/java/org/traccar/storage/StorageException.java32
-rw-r--r--src/main/java/org/traccar/storage/StorageName.java (renamed from src/main/java/org/traccar/database/QueryExtended.java)10
-rw-r--r--src/main/java/org/traccar/storage/query/Columns.java81
-rw-r--r--src/main/java/org/traccar/storage/query/Condition.java211
-rw-r--r--src/main/java/org/traccar/storage/query/Limit.java (renamed from src/main/java/org/traccar/database/OrderManager.java)16
-rw-r--r--src/main/java/org/traccar/storage/query/Order.java46
-rw-r--r--src/main/java/org/traccar/storage/query/Request.java58
-rw-r--r--src/main/java/org/traccar/web/ConsoleServlet.java16
-rw-r--r--src/main/java/org/traccar/web/ThrottlingFilter.java53
-rw-r--r--src/main/java/org/traccar/web/WebInjectionManagerFactory.java50
-rw-r--r--src/main/java/org/traccar/web/WebModule.java30
-rw-r--r--src/main/java/org/traccar/web/WebServer.java105
-rw-r--r--src/main/resources/META-INF/services/org.glassfish.jersey.internal.inject.InjectionManagerFactory1
788 files changed, 19276 insertions, 9873 deletions
diff --git a/src/main/java/org/traccar/BasePipelineFactory.java b/src/main/java/org/traccar/BasePipelineFactory.java
index c9f3a2346..b184da45c 100644
--- a/src/main/java/org/traccar/BasePipelineFactory.java
+++ b/src/main/java/org/traccar/BasePipelineFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
*/
package org.traccar;
+import com.google.inject.Injector;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInboundHandler;
@@ -22,6 +23,7 @@ import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOutboundHandler;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.timeout.IdleStateHandler;
+import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.handler.ComputedAttributesHandler;
import org.traccar.handler.CopyAttributesHandler;
@@ -43,10 +45,11 @@ import org.traccar.handler.events.AlertEventHandler;
import org.traccar.handler.events.BehaviorEventHandler;
import org.traccar.handler.events.CommandResultEventHandler;
import org.traccar.handler.events.DriverEventHandler;
-import org.traccar.handler.events.FuelDropEventHandler;
+import org.traccar.handler.events.FuelEventHandler;
import org.traccar.handler.events.GeofenceEventHandler;
import org.traccar.handler.events.IgnitionEventHandler;
import org.traccar.handler.events.MaintenanceEventHandler;
+import org.traccar.handler.events.MediaEventHandler;
import org.traccar.handler.events.MotionEventHandler;
import org.traccar.handler.events.OverspeedEventHandler;
@@ -54,26 +57,30 @@ import java.util.Map;
public abstract class BasePipelineFactory extends ChannelInitializer<Channel> {
- private final TrackerServer server;
+ private final Injector injector;
+ private final TrackerConnector connector;
private final String protocol;
private int timeout;
- public BasePipelineFactory(TrackerServer server, String protocol) {
- this.server = server;
+ public BasePipelineFactory(TrackerConnector connector, Config config, String protocol) {
+ this.injector = Main.getInjector();
+ this.connector = connector;
this.protocol = protocol;
- timeout = Context.getConfig().getInteger(Keys.PROTOCOL_TIMEOUT.withPrefix(protocol));
+ timeout = config.getInteger(Keys.PROTOCOL_TIMEOUT.withPrefix(protocol));
if (timeout == 0) {
- timeout = Context.getConfig().getInteger(Keys.SERVER_TIMEOUT);
+ timeout = config.getInteger(Keys.SERVER_TIMEOUT);
}
}
+ protected abstract void addTransportHandlers(PipelineBuilder pipeline);
+
protected abstract void addProtocolHandlers(PipelineBuilder pipeline);
@SafeVarargs
- private final void addHandlers(ChannelPipeline pipeline, Class<? extends ChannelHandler>... handlerClasses) {
+ private void addHandlers(ChannelPipeline pipeline, Class<? extends ChannelHandler>... handlerClasses) {
for (Class<? extends ChannelHandler> handlerClass : handlerClasses) {
if (handlerClass != null) {
- pipeline.addLast(Main.getInjector().getInstance(handlerClass));
+ pipeline.addLast(injector.getInstance(handlerClass));
}
}
}
@@ -97,15 +104,19 @@ public abstract class BasePipelineFactory extends ChannelInitializer<Channel> {
protected void initChannel(Channel channel) {
final ChannelPipeline pipeline = channel.pipeline();
- if (timeout > 0 && !server.isDatagram()) {
+ addTransportHandlers(pipeline::addLast);
+
+ if (timeout > 0 && !connector.isDatagram()) {
pipeline.addLast(new IdleStateHandler(timeout, 0, 0));
}
- pipeline.addLast(new OpenChannelHandler(server));
+ pipeline.addLast(new OpenChannelHandler(connector));
pipeline.addLast(new NetworkMessageHandler());
pipeline.addLast(new StandardLoggingHandler(protocol));
addProtocolHandlers(handler -> {
- if (!(handler instanceof BaseProtocolDecoder || handler instanceof BaseProtocolEncoder)) {
+ if (handler instanceof BaseProtocolDecoder || handler instanceof BaseProtocolEncoder) {
+ injector.injectMembers(handler);
+ } else {
if (handler instanceof ChannelInboundHandler) {
handler = new WrapperInboundHandler((ChannelInboundHandler) handler);
} else {
@@ -129,20 +140,20 @@ public abstract class BasePipelineFactory extends ChannelInitializer<Channel> {
CopyAttributesHandler.class,
EngineHoursHandler.class,
ComputedAttributesHandler.class,
- WebDataHandler.class,
+ PositionForwardingHandler.class,
DefaultDataHandler.class,
+ MediaEventHandler.class,
CommandResultEventHandler.class,
OverspeedEventHandler.class,
BehaviorEventHandler.class,
- FuelDropEventHandler.class,
+ FuelEventHandler.class,
MotionEventHandler.class,
GeofenceEventHandler.class,
AlertEventHandler.class,
IgnitionEventHandler.class,
MaintenanceEventHandler.class,
- DriverEventHandler.class);
-
- pipeline.addLast(new MainEventHandler());
+ DriverEventHandler.class,
+ MainEventHandler.class);
}
}
diff --git a/src/main/java/org/traccar/BaseProtocol.java b/src/main/java/org/traccar/BaseProtocol.java
index bd3391822..d19fc307e 100644
--- a/src/main/java/org/traccar/BaseProtocol.java
+++ b/src/main/java/org/traccar/BaseProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,7 +21,10 @@ import io.netty.channel.Channel;
import io.netty.handler.codec.string.StringEncoder;
import org.traccar.helper.DataConverter;
import org.traccar.model.Command;
+import org.traccar.sms.SmsManager;
+import javax.annotation.Nullable;
+import javax.inject.Inject;
import java.net.SocketAddress;
import java.util.Arrays;
import java.util.Collection;
@@ -35,7 +38,9 @@ public abstract class BaseProtocol implements Protocol {
private final String name;
private final Set<String> supportedDataCommands = new HashSet<>();
private final Set<String> supportedTextCommands = new HashSet<>();
- private final List<TrackerServer> serverList = new LinkedList<>();
+ private final List<TrackerConnector> connectorList = new LinkedList<>();
+
+ private SmsManager smsManager;
private StringProtocolEncoder textCommandEncoder = null;
@@ -48,18 +53,27 @@ public abstract class BaseProtocol implements Protocol {
name = nameFromClass(getClass());
}
+ @Inject
+ public void setSmsManager(@Nullable SmsManager smsManager) {
+ this.smsManager = smsManager;
+ }
+
@Override
public String getName() {
return name;
}
protected void addServer(TrackerServer server) {
- serverList.add(server);
+ connectorList.add(server);
+ }
+
+ protected void addClient(TrackerClient client) {
+ connectorList.add(client);
}
@Override
- public Collection<TrackerServer> getServerList() {
- return serverList;
+ public Collection<TrackerConnector> getConnectorList() {
+ return connectorList;
}
public void setSupportedDataCommands(String... commands) {
@@ -107,13 +121,13 @@ public abstract class BaseProtocol implements Protocol {
@Override
public void sendTextCommand(String destAddress, Command command) throws Exception {
- if (Context.getSmsManager() != null) {
+ if (smsManager != null) {
if (command.getType().equals(Command.TYPE_CUSTOM)) {
- Context.getSmsManager().sendMessageSync(destAddress, command.getString(Command.KEY_DATA), true);
+ smsManager.sendMessage(destAddress, command.getString(Command.KEY_DATA), true);
} else if (supportedTextCommands.contains(command.getType()) && textCommandEncoder != null) {
String encodedCommand = (String) textCommandEncoder.encodeCommand(command);
if (encodedCommand != null) {
- Context.getSmsManager().sendMessageSync(destAddress, encodedCommand, true);
+ smsManager.sendMessage(destAddress, encodedCommand, true);
} else {
throw new RuntimeException("Failed to encode command");
}
diff --git a/src/main/java/org/traccar/BaseProtocolDecoder.java b/src/main/java/org/traccar/BaseProtocolDecoder.java
index a40756796..382daf92f 100644
--- a/src/main/java/org/traccar/BaseProtocolDecoder.java
+++ b/src/main/java/org/traccar/BaseProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,47 +15,82 @@
*/
package org.traccar;
+import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
-import io.netty.channel.socket.DatagramChannel;
-import io.netty.handler.codec.http.HttpRequestDecoder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.database.CommandsManager;
-import org.traccar.database.ConnectionManager;
-import org.traccar.database.IdentityManager;
+import org.traccar.database.MediaManager;
import org.traccar.database.StatisticsManager;
import org.traccar.helper.UnitsConverter;
+import org.traccar.helper.model.AttributeUtil;
import org.traccar.model.Command;
import org.traccar.model.Device;
import org.traccar.model.Position;
+import org.traccar.session.ConnectionManager;
+import org.traccar.session.DeviceSession;
+import org.traccar.session.cache.CacheManager;
+import org.traccar.storage.StorageException;
+import javax.inject.Inject;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Collection;
import java.util.Date;
-import java.util.HashMap;
import java.util.HashSet;
-import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
- private static final Logger LOGGER = LoggerFactory.getLogger(BaseProtocolDecoder.class);
-
private static final String PROTOCOL_UNKNOWN = "unknown";
- private final Config config = Context.getConfig();
- private final IdentityManager identityManager = Context.getIdentityManager();
- private final ConnectionManager connectionManager = Context.getConnectionManager();
- private final StatisticsManager statisticsManager;
private final Protocol protocol;
+ private CacheManager cacheManager;
+ private ConnectionManager connectionManager;
+ private StatisticsManager statisticsManager;
+ private MediaManager mediaManager;
+ private CommandsManager commandsManager;
+
public BaseProtocolDecoder(Protocol protocol) {
this.protocol = protocol;
- statisticsManager = Main.getInjector() != null ? Main.getInjector().getInstance(StatisticsManager.class) : null;
+ }
+
+ public CacheManager getCacheManager() {
+ return cacheManager;
+ }
+
+ @Inject
+ public void setCacheManager(CacheManager cacheManager) {
+ this.cacheManager = cacheManager;
+ }
+
+ @Inject
+ public void setConnectionManager(ConnectionManager connectionManager) {
+ this.connectionManager = connectionManager;
+ }
+
+ @Inject
+ public void setStatisticsManager(StatisticsManager statisticsManager) {
+ this.statisticsManager = statisticsManager;
+ }
+
+ @Inject
+ public void setMediaManager(MediaManager mediaManager) {
+ this.mediaManager = mediaManager;
+ }
+
+ @Inject
+ public void setCommandsManager(CommandsManager commandsManager) {
+ this.commandsManager = commandsManager;
+ }
+
+ public CommandsManager getCommandsManager() {
+ return commandsManager;
+ }
+
+ public String writeMediaFile(String uniqueId, ByteBuf buf, String extension) {
+ return mediaManager.writeFile(uniqueId, buf, extension);
}
public String getProtocolName() {
@@ -63,7 +98,7 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
}
public String getServer(Channel channel, char delimiter) {
- String server = config.getString(Keys.PROTOCOL_SERVER.withPrefix(getProtocolName()));
+ String server = getConfig().getString(Keys.PROTOCOL_SERVER.withPrefix(getProtocolName()));
if (server == null && channel != null) {
InetSocketAddress address = (InetSocketAddress) channel.localAddress();
server = address.getAddress().getHostAddress() + ":" + address.getPort();
@@ -72,7 +107,7 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
}
protected double convertSpeed(double value, String defaultUnits) {
- switch (config.getString(getProtocolName() + ".speed", defaultUnits)) {
+ switch (getConfig().getString(getProtocolName() + ".speed", defaultUnits)) {
case "kmh":
return UnitsConverter.knotsFromKph(value);
case "mps":
@@ -91,101 +126,18 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
protected TimeZone getTimeZone(long deviceId, String defaultTimeZone) {
TimeZone result = TimeZone.getTimeZone(defaultTimeZone);
- String timeZoneName = identityManager.lookupAttributeString(deviceId, "decoder.timezone", null, false, true);
+ String timeZoneName = AttributeUtil.lookup(cacheManager, Keys.DECODER_TIMEZONE, deviceId);
if (timeZoneName != null) {
result = TimeZone.getTimeZone(timeZoneName);
}
return result;
}
- private DeviceSession channelDeviceSession; // connection-based protocols
- private final Map<SocketAddress, DeviceSession> addressDeviceSessions = new HashMap<>(); // connectionless protocols
-
- private long findDeviceId(SocketAddress remoteAddress, String... uniqueIds) {
- if (uniqueIds.length > 0) {
- long deviceId = 0;
- Device device = null;
- try {
- for (String uniqueId : uniqueIds) {
- if (uniqueId != null) {
- device = identityManager.getByUniqueId(uniqueId);
- if (device != null) {
- deviceId = device.getId();
- break;
- }
- }
- }
- } catch (Exception e) {
- LOGGER.warn("Find device error", e);
- }
- if (deviceId == 0 && config.getBoolean(Keys.DATABASE_REGISTER_UNKNOWN)) {
- return identityManager.addUnknownDevice(uniqueIds[0]);
- }
- if (device != null && !device.getDisabled()) {
- return deviceId;
- }
- StringBuilder message = new StringBuilder();
- if (deviceId == 0) {
- message.append("Unknown device -");
- } else {
- message.append("Disabled device -");
- }
- for (String uniqueId : uniqueIds) {
- message.append(" ").append(uniqueId);
- }
- if (remoteAddress != null) {
- message.append(" (").append(((InetSocketAddress) remoteAddress).getHostString()).append(")");
- }
- LOGGER.warn(message.toString());
- }
- return 0;
- }
-
public DeviceSession getDeviceSession(Channel channel, SocketAddress remoteAddress, String... uniqueIds) {
- return getDeviceSession(channel, remoteAddress, false, uniqueIds);
- }
-
- public DeviceSession getDeviceSession(
- Channel channel, SocketAddress remoteAddress, boolean ignoreCache, String... uniqueIds) {
- if (channel != null && BasePipelineFactory.getHandler(channel.pipeline(), HttpRequestDecoder.class) != null
- || ignoreCache || config.getBoolean(Keys.PROTOCOL_IGNORE_SESSIONS_CACHE.withPrefix(getProtocolName()))
- || config.getBoolean(Keys.DECODER_IGNORE_SESSIONS_CACHE)) {
- long deviceId = findDeviceId(remoteAddress, uniqueIds);
- if (deviceId != 0) {
- if (connectionManager != null) {
- connectionManager.addActiveDevice(deviceId, protocol, channel, remoteAddress);
- }
- return new DeviceSession(deviceId);
- } else {
- return null;
- }
- }
- if (channel instanceof DatagramChannel) {
- long deviceId = findDeviceId(remoteAddress, uniqueIds);
- DeviceSession deviceSession = addressDeviceSessions.get(remoteAddress);
- if (deviceSession != null && (deviceSession.getDeviceId() == deviceId || uniqueIds.length == 0)) {
- return deviceSession;
- } else if (deviceId != 0) {
- deviceSession = new DeviceSession(deviceId);
- addressDeviceSessions.put(remoteAddress, deviceSession);
- if (connectionManager != null) {
- connectionManager.addActiveDevice(deviceId, protocol, channel, remoteAddress);
- }
- return deviceSession;
- } else {
- return null;
- }
- } else {
- if (channelDeviceSession == null) {
- long deviceId = findDeviceId(remoteAddress, uniqueIds);
- if (deviceId != 0) {
- channelDeviceSession = new DeviceSession(deviceId);
- if (connectionManager != null) {
- connectionManager.addActiveDevice(deviceId, protocol, channel, remoteAddress);
- }
- }
- }
- return channelDeviceSession;
+ try {
+ return connectionManager.getDeviceSession(protocol, channel, remoteAddress, uniqueIds);
+ } catch (StorageException e) {
+ throw new RuntimeException(e);
}
}
@@ -193,7 +145,7 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
if (position.getDeviceId() != 0) {
position.setOutdated(true);
- Position last = identityManager.getLastPosition(position.getDeviceId());
+ Position last = cacheManager.getPosition(position.getDeviceId());
if (last != null) {
position.setFixTime(last.getFixTime());
position.setValid(last.getValid());
@@ -245,18 +197,15 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
}
protected void sendQueuedCommands(Channel channel, SocketAddress remoteAddress, long deviceId) {
- CommandsManager commandsManager = Context.getCommandsManager();
- if (commandsManager != null) {
- for (Command command : commandsManager.readQueuedCommands(deviceId)) {
- protocol.sendDataCommand(channel, remoteAddress, command);
- }
+ for (Command command : commandsManager.readQueuedCommands(deviceId)) {
+ protocol.sendDataCommand(channel, remoteAddress, command);
}
}
@Override
protected Object handleEmptyMessage(Channel channel, SocketAddress remoteAddress, Object msg) {
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
- if (config.getBoolean(Keys.DATABASE_SAVE_EMPTY) && deviceSession != null) {
+ if (getConfig().getBoolean(Keys.DATABASE_SAVE_EMPTY) && deviceSession != null) {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
getLastLocation(position, null);
diff --git a/src/main/java/org/traccar/BaseProtocolEncoder.java b/src/main/java/org/traccar/BaseProtocolEncoder.java
index b6df07b98..9c3934184 100644
--- a/src/main/java/org/traccar/BaseProtocolEncoder.java
+++ b/src/main/java/org/traccar/BaseProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,7 +21,13 @@ import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.traccar.helper.NetworkUtil;
+import org.traccar.helper.model.AttributeUtil;
import org.traccar.model.Command;
+import org.traccar.model.Device;
+import org.traccar.session.cache.CacheManager;
+
+import javax.inject.Inject;
public abstract class BaseProtocolEncoder extends ChannelOutboundHandlerAdapter {
@@ -31,22 +37,33 @@ public abstract class BaseProtocolEncoder extends ChannelOutboundHandlerAdapter
private final Protocol protocol;
+ private CacheManager cacheManager;
+
public BaseProtocolEncoder(Protocol protocol) {
this.protocol = protocol;
}
+ public CacheManager getCacheManager() {
+ return cacheManager;
+ }
+
+ @Inject
+ public void setCacheManager(CacheManager cacheManager) {
+ this.cacheManager = cacheManager;
+ }
+
public String getProtocolName() {
return protocol != null ? protocol.getName() : PROTOCOL_UNKNOWN;
}
protected String getUniqueId(long deviceId) {
- return Context.getIdentityManager().getById(deviceId).getUniqueId();
+ return cacheManager.getObject(Device.class, deviceId).getUniqueId();
}
protected void initDevicePassword(Command command, String defaultPassword) {
- if (!command.getAttributes().containsKey(Command.KEY_DEVICE_PASSWORD)) {
- String password = Context.getIdentityManager()
- .getDevicePassword(command.getDeviceId(), getProtocolName(), defaultPassword);
+ if (!command.hasAttribute(Command.KEY_DEVICE_PASSWORD)) {
+ String password = AttributeUtil.getDevicePassword(
+ cacheManager, command.getDeviceId(), getProtocolName(), defaultPassword);
command.set(Command.KEY_DEVICE_PASSWORD, password);
}
}
@@ -62,7 +79,7 @@ public abstract class BaseProtocolEncoder extends ChannelOutboundHandlerAdapter
Object encodedCommand = encodeCommand(ctx.channel(), command);
StringBuilder s = new StringBuilder();
- s.append("[").append(ctx.channel().id().asShortText()).append("] ");
+ 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) {
diff --git a/src/main/java/org/traccar/BaseProtocolPoller.java b/src/main/java/org/traccar/BaseProtocolPoller.java
new file mode 100644
index 000000000..be6556374
--- /dev/null
+++ b/src/main/java/org/traccar/BaseProtocolPoller.java
@@ -0,0 +1,55 @@
+/*
+ * 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;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelDuplexHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.util.concurrent.Future;
+
+import java.net.SocketAddress;
+import java.util.concurrent.TimeUnit;
+
+public abstract class BaseProtocolPoller extends ChannelDuplexHandler {
+
+ private final long interval;
+ private Future<?> timeout;
+
+ public BaseProtocolPoller(long interval) {
+ this.interval = interval;
+ }
+
+ protected abstract void sendRequest(Channel channel, SocketAddress remoteAddress);
+
+ @Override
+ public void channelActive(ChannelHandlerContext ctx) throws Exception {
+ super.channelActive(ctx);
+ if (interval > 0) {
+ timeout = ctx.executor().scheduleAtFixedRate(
+ () -> sendRequest(ctx.channel(), ctx.channel().remoteAddress()), 0, interval, TimeUnit.SECONDS);
+ }
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ super.channelInactive(ctx);
+ if (timeout != null) {
+ timeout.cancel(false);
+ timeout = null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/Context.java b/src/main/java/org/traccar/Context.java
deleted file mode 100644
index aeba9c4c9..000000000
--- a/src/main/java/org/traccar/Context.java
+++ /dev/null
@@ -1,416 +0,0 @@
-/*
- * Copyright 2015 - 2021 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.SerializationFeature;
-import com.fasterxml.jackson.datatype.jsr353.JSR353Module;
-import org.apache.velocity.app.VelocityEngine;
-import org.eclipse.jetty.util.URIUtil;
-import org.traccar.config.Config;
-import org.traccar.config.Keys;
-import org.traccar.database.AttributesManager;
-import org.traccar.database.BaseObjectManager;
-import org.traccar.database.CalendarManager;
-import org.traccar.database.CommandsManager;
-import org.traccar.database.ConnectionManager;
-import org.traccar.database.DataManager;
-import org.traccar.database.DeviceManager;
-import org.traccar.database.DriversManager;
-import org.traccar.database.GeofenceManager;
-import org.traccar.database.GroupsManager;
-import org.traccar.database.IdentityManager;
-import org.traccar.database.LdapProvider;
-import org.traccar.database.MailManager;
-import org.traccar.database.MaintenancesManager;
-import org.traccar.database.MediaManager;
-import org.traccar.database.NotificationManager;
-import org.traccar.database.OrderManager;
-import org.traccar.database.PermissionsManager;
-import org.traccar.database.UsersManager;
-import org.traccar.geocoder.Geocoder;
-import org.traccar.helper.Log;
-import org.traccar.helper.SanitizerModule;
-import org.traccar.model.Attribute;
-import org.traccar.model.BaseModel;
-import org.traccar.model.Calendar;
-import org.traccar.model.Command;
-import org.traccar.model.Device;
-import org.traccar.model.Driver;
-import org.traccar.model.Geofence;
-import org.traccar.model.Group;
-import org.traccar.model.Maintenance;
-import org.traccar.model.Notification;
-import org.traccar.model.Order;
-import org.traccar.model.User;
-import org.traccar.notification.EventForwarder;
-import org.traccar.notification.NotificatorManager;
-import org.traccar.reports.model.TripsConfig;
-import org.traccar.schedule.ScheduleManager;
-import org.traccar.sms.HttpSmsClient;
-import org.traccar.sms.SmsManager;
-import org.traccar.sms.SnsSmsClient;
-import org.traccar.web.WebServer;
-
-import javax.ws.rs.client.Client;
-import javax.ws.rs.client.ClientBuilder;
-import javax.ws.rs.ext.ContextResolver;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.Properties;
-
-public final class Context {
-
- private Context() {
- }
-
- private static Config config;
-
- public static Config getConfig() {
- return config;
- }
-
- private static ObjectMapper objectMapper;
-
- public static ObjectMapper getObjectMapper() {
- return objectMapper;
- }
-
- private static IdentityManager identityManager;
-
- public static IdentityManager getIdentityManager() {
- return identityManager;
- }
-
- private static DataManager dataManager;
-
- public static DataManager getDataManager() {
- return dataManager;
- }
-
- private static LdapProvider ldapProvider;
-
- public static LdapProvider getLdapProvider() {
- return ldapProvider;
- }
-
- private static MailManager mailManager;
-
- public static MailManager getMailManager() {
- return mailManager;
- }
-
- private static MediaManager mediaManager;
-
- public static MediaManager getMediaManager() {
- return mediaManager;
- }
-
- private static UsersManager usersManager;
-
- public static UsersManager getUsersManager() {
- return usersManager;
- }
-
- private static GroupsManager groupsManager;
-
- public static GroupsManager getGroupsManager() {
- return groupsManager;
- }
-
- private static DeviceManager deviceManager;
-
- public static DeviceManager getDeviceManager() {
- return deviceManager;
- }
-
- private static ConnectionManager connectionManager;
-
- public static ConnectionManager getConnectionManager() {
- return connectionManager;
- }
-
- private static PermissionsManager permissionsManager;
-
- public static PermissionsManager getPermissionsManager() {
- return permissionsManager;
- }
-
- public static Geocoder getGeocoder() {
- return Main.getInjector() != null ? Main.getInjector().getInstance(Geocoder.class) : null;
- }
-
- private static WebServer webServer;
-
- public static WebServer getWebServer() {
- return webServer;
- }
-
- private static ServerManager serverManager;
-
- public static ServerManager getServerManager() {
- return serverManager;
- }
-
- private static ScheduleManager scheduleManager;
-
- public static ScheduleManager getScheduleManager() {
- return scheduleManager;
- }
-
- private static GeofenceManager geofenceManager;
-
- public static GeofenceManager getGeofenceManager() {
- return geofenceManager;
- }
-
- private static CalendarManager calendarManager;
-
- public static CalendarManager getCalendarManager() {
- return calendarManager;
- }
-
- private static NotificationManager notificationManager;
-
- public static NotificationManager getNotificationManager() {
- return notificationManager;
- }
-
- private static NotificatorManager notificatorManager;
-
- public static NotificatorManager getNotificatorManager() {
- return notificatorManager;
- }
-
- private static VelocityEngine velocityEngine;
-
- public static VelocityEngine getVelocityEngine() {
- return velocityEngine;
- }
-
- private static Client client = ClientBuilder.newClient();
-
- public static Client getClient() {
- return client;
- }
-
- private static EventForwarder eventForwarder;
-
- public static EventForwarder getEventForwarder() {
- return eventForwarder;
- }
-
- private static AttributesManager attributesManager;
-
- public static AttributesManager getAttributesManager() {
- return attributesManager;
- }
-
- private static DriversManager driversManager;
-
- public static DriversManager getDriversManager() {
- return driversManager;
- }
-
- private static CommandsManager commandsManager;
-
- public static CommandsManager getCommandsManager() {
- return commandsManager;
- }
-
- private static MaintenancesManager maintenancesManager;
-
- public static MaintenancesManager getMaintenancesManager() {
- return maintenancesManager;
- }
-
- private static OrderManager orderManager;
-
- public static OrderManager getOrderManager() {
- return orderManager;
- }
-
- private static SmsManager smsManager;
-
- public static SmsManager getSmsManager() {
- return smsManager;
- }
-
- private static TripsConfig tripsConfig;
-
- public static TripsConfig getTripsConfig() {
- return tripsConfig;
- }
-
- public static TripsConfig initTripsConfig() {
- return new TripsConfig(
- config.getLong(Keys.REPORT_TRIP_MINIMAL_TRIP_DISTANCE),
- config.getLong(Keys.REPORT_TRIP_MINIMAL_TRIP_DURATION) * 1000,
- config.getLong(Keys.REPORT_TRIP_MINIMAL_PARKING_DURATION) * 1000,
- config.getLong(Keys.REPORT_TRIP_MINIMAL_NO_DATA_DURATION) * 1000,
- config.getBoolean(Keys.REPORT_TRIP_USE_IGNITION),
- config.getBoolean(Keys.EVENT_MOTION_PROCESS_INVALID_POSITIONS),
- config.getDouble(Keys.EVENT_MOTION_SPEED_THRESHOLD));
- }
-
- private static class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
-
- @Override
- public ObjectMapper getContext(Class<?> clazz) {
- return objectMapper;
- }
-
- }
-
- public static void init(String configFile) throws Exception {
-
- try {
- config = new Config(configFile);
- Log.setupLogger(config);
- } catch (Exception e) {
- config = new Config();
- Log.setupDefaultLogger();
- throw e;
- }
-
- objectMapper = new ObjectMapper();
- objectMapper.registerModule(new SanitizerModule());
- objectMapper.registerModule(new JSR353Module());
- objectMapper.setConfig(
- objectMapper.getSerializationConfig().without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS));
-
- client = ClientBuilder.newClient().register(new ObjectMapperContextResolver());
-
- if (config.hasKey(Keys.DATABASE_URL)) {
- dataManager = new DataManager(config);
- }
-
- if (config.hasKey(Keys.LDAP_URL)) {
- ldapProvider = new LdapProvider(config);
- }
-
- mailManager = new MailManager();
-
- mediaManager = new MediaManager(config.getString(Keys.MEDIA_PATH));
-
- if (dataManager != null) {
- usersManager = new UsersManager(dataManager);
- groupsManager = new GroupsManager(dataManager);
- deviceManager = new DeviceManager(dataManager);
- }
-
- identityManager = deviceManager;
-
- if (config.hasKey(Keys.WEB_PORT)) {
- webServer = new WebServer(config);
- }
-
- permissionsManager = new PermissionsManager(dataManager, usersManager);
-
- connectionManager = new ConnectionManager();
-
- tripsConfig = initTripsConfig();
-
- if (config.hasKey(Keys.SMS_HTTP_URL)) {
- smsManager = new HttpSmsClient();
- } else if (config.hasKey(Keys.SMS_AWS_REGION)) {
- smsManager = new SnsSmsClient();
- }
-
- initEventsModule();
-
- serverManager = new ServerManager();
- scheduleManager = new ScheduleManager();
-
- if (config.hasKey(Keys.EVENT_FORWARD_URL)) {
- eventForwarder = new EventForwarder();
- }
-
- attributesManager = new AttributesManager(dataManager);
-
- driversManager = new DriversManager(dataManager);
-
- commandsManager = new CommandsManager(dataManager, config.getBoolean(Keys.COMMANDS_QUEUEING));
-
- orderManager = new OrderManager(dataManager);
-
- }
-
- private static void initEventsModule() {
-
- geofenceManager = new GeofenceManager(dataManager);
- calendarManager = new CalendarManager(dataManager);
- maintenancesManager = new MaintenancesManager(dataManager);
- notificationManager = new NotificationManager(dataManager);
- notificatorManager = new NotificatorManager();
- Properties velocityProperties = new Properties();
- velocityProperties.setProperty("file.resource.loader.path",
- Context.getConfig().getString("templates.rootPath", "templates") + "/");
- velocityProperties.setProperty("runtime.log.logsystem.class",
- "org.apache.velocity.runtime.log.NullLogChute");
-
- String address;
- try {
- address = config.getString(Keys.WEB_ADDRESS, InetAddress.getLocalHost().getHostAddress());
- } catch (UnknownHostException e) {
- address = "localhost";
- }
-
- String webUrl = URIUtil.newURI("http", address, config.getInteger(Keys.WEB_PORT), "", "");
- webUrl = Context.getConfig().getString("web.url", webUrl);
- velocityProperties.setProperty("web.url", webUrl);
-
- velocityEngine = new VelocityEngine();
- velocityEngine.init(velocityProperties);
- }
-
- public static void init(IdentityManager testIdentityManager, MediaManager testMediaManager) {
- config = new Config();
- objectMapper = new ObjectMapper();
- objectMapper.registerModule(new JSR353Module());
- client = ClientBuilder.newClient().register(new ObjectMapperContextResolver());
- identityManager = testIdentityManager;
- mediaManager = testMediaManager;
- }
-
- public static <T extends BaseModel> BaseObjectManager<T> getManager(Class<T> clazz) {
- if (clazz.equals(Device.class)) {
- return (BaseObjectManager<T>) deviceManager;
- } else if (clazz.equals(Group.class)) {
- return (BaseObjectManager<T>) groupsManager;
- } else if (clazz.equals(User.class)) {
- return (BaseObjectManager<T>) usersManager;
- } else if (clazz.equals(Calendar.class)) {
- return (BaseObjectManager<T>) calendarManager;
- } else if (clazz.equals(Attribute.class)) {
- return (BaseObjectManager<T>) attributesManager;
- } else if (clazz.equals(Geofence.class)) {
- return (BaseObjectManager<T>) geofenceManager;
- } else if (clazz.equals(Driver.class)) {
- return (BaseObjectManager<T>) driversManager;
- } else if (clazz.equals(Command.class)) {
- return (BaseObjectManager<T>) commandsManager;
- } else if (clazz.equals(Maintenance.class)) {
- return (BaseObjectManager<T>) maintenancesManager;
- } else if (clazz.equals(Notification.class)) {
- return (BaseObjectManager<T>) notificationManager;
- } else if (clazz.equals(Order.class)) {
- return (BaseObjectManager<T>) orderManager;
- }
- return null;
- }
-
-}
diff --git a/src/main/java/org/traccar/ExtendedObjectDecoder.java b/src/main/java/org/traccar/ExtendedObjectDecoder.java
index 46720da52..f79a36c85 100644
--- a/src/main/java/org/traccar/ExtendedObjectDecoder.java
+++ b/src/main/java/org/traccar/ExtendedObjectDecoder.java
@@ -21,18 +21,38 @@ import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
+import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.helper.DataConverter;
import org.traccar.model.Position;
+import javax.inject.Inject;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
public abstract class ExtendedObjectDecoder extends ChannelInboundHandlerAdapter {
+ private Config config;
+
+ public Config getConfig() {
+ return config;
+ }
+
+ @Inject
+ public void setConfig(Config config) {
+ this.config = config;
+ init();
+ }
+
+ /**
+ * Method called when config is initialized.
+ */
+ protected void init() {
+ }
+
private void saveOriginal(Object decodedMessage, Object originalMessage) {
- if (Context.getConfig().getBoolean(Keys.DATABASE_SAVE_ORIGINAL) && decodedMessage instanceof Position) {
+ if (getConfig().getBoolean(Keys.DATABASE_SAVE_ORIGINAL) && decodedMessage instanceof Position) {
Position position = (Position) decodedMessage;
if (originalMessage instanceof ByteBuf) {
ByteBuf buf = (ByteBuf) originalMessage;
diff --git a/src/main/java/org/traccar/LifecycleObject.java b/src/main/java/org/traccar/LifecycleObject.java
new file mode 100644
index 000000000..fe0dc698a
--- /dev/null
+++ b/src/main/java/org/traccar/LifecycleObject.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar;
+
+public interface LifecycleObject {
+ void start() throws Exception;
+ void stop() throws Exception;
+}
diff --git a/src/main/java/org/traccar/Main.java b/src/main/java/org/traccar/Main.java
index 63e5c1f90..e34fbb72a 100644
--- a/src/main/java/org/traccar/Main.java
+++ b/src/main/java/org/traccar/Main.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,7 +19,13 @@ import com.google.inject.Guice;
import com.google.inject.Injector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.traccar.api.HealthCheckService;
+import org.traccar.broadcast.BroadcastService;
+import org.traccar.helper.model.DeviceUtil;
+import org.traccar.schedule.ScheduleManager;
+import org.traccar.storage.DatabaseModule;
+import org.traccar.storage.Storage;
+import org.traccar.web.WebModule;
+import org.traccar.web.WebServer;
import java.io.File;
import java.lang.management.ManagementFactory;
@@ -28,7 +34,9 @@ import java.lang.management.OperatingSystemMXBean;
import java.lang.management.RuntimeMXBean;
import java.nio.charset.Charset;
import java.util.Locale;
-import java.util.Timer;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
public final class Main {
@@ -107,40 +115,39 @@ public final class Main {
}
}
- private static void scheduleHealthCheck() {
- HealthCheckService service = new HealthCheckService();
- if (service.isEnabled()) {
- new Timer().scheduleAtFixedRate(
- service.createTask(), service.getPeriod(), service.getPeriod());
- }
- }
-
public static void run(String configFile) {
try {
- Context.init(configFile);
- injector = Guice.createInjector(new MainModule());
+ injector = Guice.createInjector(new MainModule(configFile), new DatabaseModule(), new WebModule());
logSystemInfo();
LOGGER.info("Version: " + Main.class.getPackage().getImplementationVersion());
LOGGER.info("Starting server...");
- Context.getServerManager().start();
- if (Context.getWebServer() != null) {
- Context.getWebServer().start();
+ if (injector.getInstance(BroadcastService.class).singleInstance()) {
+ DeviceUtil.resetStatus(injector.getInstance(Storage.class));
}
- Context.getScheduleManager().start();
- scheduleHealthCheck();
+ var services = Stream.of(
+ ServerManager.class, WebServer.class, ScheduleManager.class, BroadcastService.class)
+ .map(injector::getInstance)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+
+ for (var service : services) {
+ service.start();
+ }
Thread.setDefaultUncaughtExceptionHandler((t, e) -> LOGGER.error("Thread exception", e));
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
- LOGGER.info("Shutting down server...");
-
- Context.getScheduleManager().stop();
- if (Context.getWebServer() != null) {
- Context.getWebServer().stop();
+ LOGGER.info("Stopping server...");
+
+ for (var service : services) {
+ try {
+ service.stop();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
}
- Context.getServerManager().stop();
}));
} catch (Exception e) {
LOGGER.error("Main method error", e);
diff --git a/src/main/java/org/traccar/MainEventHandler.java b/src/main/java/org/traccar/MainEventHandler.java
index 4889f6a2e..877f03ae7 100644
--- a/src/main/java/org/traccar/MainEventHandler.java
+++ b/src/main/java/org/traccar/MainEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
package org.traccar;
import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.socket.DatagramChannel;
@@ -23,17 +24,31 @@ import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.timeout.IdleStateEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.database.StatisticsManager;
import org.traccar.helper.DateUtil;
+import org.traccar.helper.NetworkUtil;
+import org.traccar.helper.model.PositionUtil;
+import org.traccar.model.Device;
import org.traccar.model.Position;
-
-import java.sql.SQLException;
+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 java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
+@Singleton
+@ChannelHandler.Sharable
public class MainEventHandler extends ChannelInboundHandlerAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(MainEventHandler.class);
@@ -41,13 +56,24 @@ public class MainEventHandler extends ChannelInboundHandlerAdapter {
private final Set<String> connectionlessProtocols = new HashSet<>();
private final Set<String> logAttributes = new LinkedHashSet<>();
- public MainEventHandler() {
- String connectionlessProtocolList = Context.getConfig().getString(Keys.STATUS_IGNORE_OFFLINE);
+ private final CacheManager cacheManager;
+ private final Storage storage;
+ private final ConnectionManager connectionManager;
+ private final StatisticsManager statisticsManager;
+
+ @Inject
+ public MainEventHandler(
+ Config config, CacheManager cacheManager, Storage storage, ConnectionManager connectionManager,
+ StatisticsManager statisticsManager) {
+ this.cacheManager = cacheManager;
+ this.storage = storage;
+ this.connectionManager = connectionManager;
+ this.statisticsManager = statisticsManager;
+ String connectionlessProtocolList = config.getString(Keys.STATUS_IGNORE_OFFLINE);
if (connectionlessProtocolList != null) {
connectionlessProtocols.addAll(Arrays.asList(connectionlessProtocolList.split("[, ]")));
}
- logAttributes.addAll(Arrays.asList(
- Context.getConfig().getString(Keys.LOGGER_ATTRIBUTES).split("[, ]")));
+ logAttributes.addAll(Arrays.asList(config.getString(Keys.LOGGER_ATTRIBUTES).split("[, ]")));
}
@Override
@@ -55,17 +81,27 @@ public class MainEventHandler extends ChannelInboundHandlerAdapter {
if (msg instanceof Position) {
Position position = (Position) msg;
+ Device device = cacheManager.getObject(Device.class, position.getDeviceId());
+
try {
- Context.getDeviceManager().updateLatestPosition(position);
- } catch (SQLException error) {
+ if (PositionUtil.isLatest(cacheManager, position)) {
+ Device updatedDevice = new Device();
+ updatedDevice.setId(position.getDeviceId());
+ updatedDevice.setPositionId(position.getId());
+ storage.updateObject(updatedDevice, new Request(
+ new Columns.Include("positionId"),
+ new Condition.Equals("id", updatedDevice.getId())));
+
+ cacheManager.updatePosition(position);
+ connectionManager.updatePosition(true, position);
+ }
+ } catch (StorageException error) {
LOGGER.warn("Failed to update device", error);
}
- String uniqueId = Context.getIdentityManager().getById(position.getDeviceId()).getUniqueId();
-
StringBuilder builder = new StringBuilder();
- builder.append(formatChannel(ctx.channel())).append(" ");
- builder.append("id: ").append(uniqueId);
+ builder.append("[").append(NetworkUtil.session(ctx.channel())).append("] ");
+ builder.append("id: ").append(device.getUniqueId());
for (String attribute : logAttributes) {
switch (attribute) {
case "time":
@@ -108,31 +144,25 @@ public class MainEventHandler extends ChannelInboundHandlerAdapter {
}
LOGGER.info(builder.toString());
- Main.getInjector().getInstance(StatisticsManager.class)
- .registerMessageStored(position.getDeviceId(), position.getProtocol());
+ statisticsManager.registerMessageStored(position.getDeviceId(), position.getProtocol());
}
}
- private static String formatChannel(Channel channel) {
- return String.format("[%s]", channel.id().asShortText());
- }
-
@Override
public void channelActive(ChannelHandlerContext ctx) {
if (!(ctx.channel() instanceof DatagramChannel)) {
- LOGGER.info(formatChannel(ctx.channel()) + " connected");
+ LOGGER.info("[{}] connected", NetworkUtil.session(ctx.channel()));
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) {
- LOGGER.info(formatChannel(ctx.channel()) + " disconnected");
+ LOGGER.info("[{}] disconnected", NetworkUtil.session(ctx.channel()));
closeChannel(ctx.channel());
- if (BasePipelineFactory.getHandler(ctx.pipeline(), HttpRequestDecoder.class) == null
- && !connectionlessProtocols.contains(ctx.pipeline().get(BaseProtocolDecoder.class).getProtocolName())) {
- Context.getConnectionManager().removeActiveDevice(ctx.channel());
- }
+ boolean supportsOffline = BasePipelineFactory.getHandler(ctx.pipeline(), HttpRequestDecoder.class) == null
+ && !connectionlessProtocols.contains(ctx.pipeline().get(BaseProtocolDecoder.class).getProtocolName());
+ connectionManager.deviceDisconnected(ctx.channel(), supportsOffline);
}
@Override
@@ -140,14 +170,14 @@ public class MainEventHandler extends ChannelInboundHandlerAdapter {
while (cause.getCause() != null && cause.getCause() != cause) {
cause = cause.getCause();
}
- LOGGER.warn(formatChannel(ctx.channel()) + " error", cause);
+ LOGGER.info("[{}] error", NetworkUtil.session(ctx.channel()), cause);
closeChannel(ctx.channel());
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
if (evt instanceof IdleStateEvent) {
- LOGGER.info(formatChannel(ctx.channel()) + " timed out");
+ LOGGER.info("[{}] timed out", NetworkUtil.session(ctx.channel()));
closeChannel(ctx.channel());
}
}
diff --git a/src/main/java/org/traccar/MainModule.java b/src/main/java/org/traccar/MainModule.java
index 11100f66e..55211d109 100644
--- a/src/main/java/org/traccar/MainModule.java
+++ b/src/main/java/org/traccar/MainModule.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,146 +16,161 @@
package org.traccar;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.datatype.jsr353.JSR353Module;
import com.google.inject.AbstractModule;
+import com.google.inject.Injector;
import com.google.inject.Provides;
-import com.google.inject.Singleton;
+import com.google.inject.Scopes;
+import com.google.inject.name.Names;
+import io.netty.util.HashedWheelTimer;
+import io.netty.util.Timer;
+import org.apache.velocity.app.VelocityEngine;
+import org.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.NullBroadcastService;
import org.traccar.config.Config;
import org.traccar.config.Keys;
-import org.traccar.database.AttributesManager;
-import org.traccar.database.CalendarManager;
-import org.traccar.database.ConnectionManager;
-import org.traccar.database.DataManager;
-import org.traccar.database.DeviceManager;
-import org.traccar.database.GeofenceManager;
-import org.traccar.database.IdentityManager;
-import org.traccar.database.MaintenancesManager;
+import org.traccar.database.LdapProvider;
import org.traccar.database.StatisticsManager;
+import org.traccar.forward.EventForwarder;
+import org.traccar.forward.EventForwarderJson;
+import org.traccar.forward.EventForwarderKafka;
+import org.traccar.forward.EventForwarderMqtt;
+import org.traccar.forward.PositionForwarder;
+import org.traccar.forward.PositionForwarderJson;
+import org.traccar.forward.PositionForwarderKafka;
+import org.traccar.forward.PositionForwarderUrl;
import org.traccar.geocoder.AddressFormat;
import org.traccar.geocoder.BanGeocoder;
import org.traccar.geocoder.BingMapsGeocoder;
import org.traccar.geocoder.FactualGeocoder;
+import org.traccar.geocoder.GeoapifyGeocoder;
import org.traccar.geocoder.GeocodeFarmGeocoder;
import org.traccar.geocoder.GeocodeXyzGeocoder;
import org.traccar.geocoder.Geocoder;
import org.traccar.geocoder.GisgraphyGeocoder;
import org.traccar.geocoder.GoogleGeocoder;
import org.traccar.geocoder.HereGeocoder;
+import org.traccar.geocoder.LocationIqGeocoder;
import org.traccar.geocoder.MapQuestGeocoder;
import org.traccar.geocoder.MapTilerGeocoder;
+import org.traccar.geocoder.MapboxGeocoder;
import org.traccar.geocoder.MapmyIndiaGeocoder;
import org.traccar.geocoder.NominatimGeocoder;
import org.traccar.geocoder.OpenCageGeocoder;
import org.traccar.geocoder.PositionStackGeocoder;
+import org.traccar.geocoder.TestGeocoder;
import org.traccar.geocoder.TomTomGeocoder;
-import org.traccar.geocoder.MapboxGeocoder;
import org.traccar.geolocation.GeolocationProvider;
import org.traccar.geolocation.GoogleGeolocationProvider;
import org.traccar.geolocation.MozillaGeolocationProvider;
import org.traccar.geolocation.OpenCellIdGeolocationProvider;
import org.traccar.geolocation.UnwiredGeolocationProvider;
-import org.traccar.handler.ComputedAttributesHandler;
-import org.traccar.handler.CopyAttributesHandler;
-import org.traccar.handler.DefaultDataHandler;
-import org.traccar.handler.DistanceHandler;
-import org.traccar.handler.EngineHoursHandler;
-import org.traccar.handler.FilterHandler;
import org.traccar.handler.GeocoderHandler;
import org.traccar.handler.GeolocationHandler;
-import org.traccar.handler.HemisphereHandler;
-import org.traccar.handler.MotionHandler;
-import org.traccar.handler.RemoteAddressHandler;
import org.traccar.handler.SpeedLimitHandler;
-import org.traccar.handler.TimeHandler;
-import org.traccar.handler.events.AlertEventHandler;
-import org.traccar.handler.events.BehaviorEventHandler;
-import org.traccar.handler.events.CommandResultEventHandler;
-import org.traccar.handler.events.DriverEventHandler;
-import org.traccar.handler.events.FuelDropEventHandler;
-import org.traccar.handler.events.GeofenceEventHandler;
-import org.traccar.handler.events.IgnitionEventHandler;
-import org.traccar.handler.events.MaintenanceEventHandler;
-import org.traccar.handler.events.MotionEventHandler;
-import org.traccar.handler.events.OverspeedEventHandler;
-import org.traccar.reports.model.TripsConfig;
+import org.traccar.helper.ObjectMapperContextResolver;
+import org.traccar.helper.SanitizerModule;
+import org.traccar.mail.LogMailManager;
+import org.traccar.mail.MailManager;
+import org.traccar.mail.SmtpMailManager;
+import org.traccar.session.cache.CacheManager;
+import org.traccar.sms.HttpSmsClient;
+import org.traccar.sms.SmsManager;
+import org.traccar.sms.SnsSmsClient;
+import org.traccar.speedlimit.OverpassSpeedLimitProvider;
+import org.traccar.speedlimit.SpeedLimitProvider;
+import org.traccar.storage.DatabaseStorage;
+import org.traccar.storage.Storage;
+import org.traccar.web.WebServer;
import javax.annotation.Nullable;
+import javax.inject.Singleton;
import javax.ws.rs.client.Client;
-import io.netty.util.Timer;
-import org.traccar.speedlimit.OverpassSpeedLimitProvider;
-import org.traccar.speedlimit.SpeedLimitProvider;
+import javax.ws.rs.client.ClientBuilder;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Properties;
public class MainModule extends AbstractModule {
- @Provides
- public static ObjectMapper provideObjectMapper() {
- return Context.getObjectMapper();
- }
-
- @Provides
- public static Config provideConfig() {
- return Context.getConfig();
- }
-
- @Provides
- public static DataManager provideDataManager() {
- return Context.getDataManager();
- }
-
- @Provides
- public static IdentityManager provideIdentityManager() {
- return Context.getIdentityManager();
- }
+ private final String configFile;
- @Provides
- public static ConnectionManager provideConnectionManager() {
- return Context.getConnectionManager();
+ public MainModule(String configFile) {
+ this.configFile = configFile;
}
- @Provides
- public static Client provideClient() {
- return Context.getClient();
- }
-
- @Provides
- public static TripsConfig provideTripsConfig() {
- return Context.getTripsConfig();
+ @Override
+ 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 DeviceManager provideDeviceManager() {
- return Context.getDeviceManager();
+ 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.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
+ return objectMapper;
}
+ @Singleton
@Provides
- public static GeofenceManager provideGeofenceManager() {
- return Context.getGeofenceManager();
+ public static Client provideClient(ObjectMapperContextResolver objectMapperContextResolver) {
+ return ClientBuilder.newClient().register(objectMapperContextResolver);
}
+ @Singleton
@Provides
- public static CalendarManager provideCalendarManager() {
- return Context.getCalendarManager();
+ public static SmsManager provideSmsManager(Config config, Client client) {
+ if (config.hasKey(Keys.SMS_HTTP_URL)) {
+ return new HttpSmsClient(config, client);
+ } else if (config.hasKey(Keys.SMS_AWS_REGION)) {
+ return new SnsSmsClient(config);
+ }
+ return null;
}
+ @Singleton
@Provides
- public static AttributesManager provideAttributesManager() {
- return Context.getAttributesManager();
+ public static MailManager provideMailManager(Config config, StatisticsManager statisticsManager) {
+ if (config.getBoolean(Keys.MAIL_DEBUG)) {
+ return new LogMailManager();
+ } else {
+ return new SmtpMailManager(config, statisticsManager);
+ }
}
+ @Singleton
@Provides
- public static MaintenancesManager provideMaintenancesManager() {
- return Context.getMaintenancesManager();
+ public static LdapProvider provideLdapProvider(Config config) {
+ if (config.hasKey(Keys.LDAP_URL)) {
+ return new LdapProvider(config);
+ }
+ return null;
}
- @Singleton
@Provides
- public static StatisticsManager provideStatisticsManager(
- Config config, DataManager dataManager, Client client, ObjectMapper objectMapper) {
- return new StatisticsManager(config, dataManager, client, objectMapper);
+ public static WebServer provideWebServer(Injector injector, Config config) {
+ if (config.hasKey(Keys.WEB_PORT)) {
+ return new WebServer(injector, config);
+ }
+ return null;
}
@Singleton
@Provides
- public static Geocoder provideGeocoder(Config config) {
+ public static Geocoder provideGeocoder(Config config, Client client, StatisticsManager statisticsManager) {
if (config.getBoolean(Keys.GEOCODER_ENABLE)) {
String type = config.getString(Keys.GEOCODER_TYPE, "google");
String url = config.getString(Keys.GEOCODER_URL);
@@ -166,60 +181,88 @@ public class MainModule extends AbstractModule {
AddressFormat addressFormat = formatString != null ? new AddressFormat(formatString) : new AddressFormat();
int cacheSize = config.getInteger(Keys.GEOCODER_CACHE_SIZE);
+ Geocoder geocoder;
switch (type) {
+ case "test":
+ geocoder = new TestGeocoder();
+ break;
case "nominatim":
- return new NominatimGeocoder(url, key, language, cacheSize, addressFormat);
+ geocoder = new NominatimGeocoder(client, url, key, language, cacheSize, addressFormat);
+ break;
+ case "locationiq":
+ geocoder = new LocationIqGeocoder(client, url, key, language, cacheSize, addressFormat);
+ break;
case "gisgraphy":
- return new GisgraphyGeocoder(url, cacheSize, addressFormat);
+ geocoder = new GisgraphyGeocoder(client, url, cacheSize, addressFormat);
+ break;
case "mapquest":
- return new MapQuestGeocoder(url, key, cacheSize, addressFormat);
+ geocoder = new MapQuestGeocoder(client, url, key, cacheSize, addressFormat);
+ break;
case "opencage":
- return new OpenCageGeocoder(url, key, cacheSize, addressFormat);
+ geocoder = new OpenCageGeocoder(client, url, key, language, cacheSize, addressFormat);
+ break;
case "bingmaps":
- return new BingMapsGeocoder(url, key, cacheSize, addressFormat);
+ geocoder = new BingMapsGeocoder(client, url, key, cacheSize, addressFormat);
+ break;
case "factual":
- return new FactualGeocoder(url, key, cacheSize, addressFormat);
+ geocoder = new FactualGeocoder(client, url, key, cacheSize, addressFormat);
+ break;
case "geocodefarm":
- return new GeocodeFarmGeocoder(key, language, cacheSize, addressFormat);
+ geocoder = new GeocodeFarmGeocoder(client, key, language, cacheSize, addressFormat);
+ break;
case "geocodexyz":
- return new GeocodeXyzGeocoder(key, cacheSize, addressFormat);
+ geocoder = new GeocodeXyzGeocoder(client, key, cacheSize, addressFormat);
+ break;
case "ban":
- return new BanGeocoder(cacheSize, addressFormat);
+ geocoder = new BanGeocoder(client, cacheSize, addressFormat);
+ break;
case "here":
- return new HereGeocoder(url, id, key, language, cacheSize, addressFormat);
+ geocoder = new HereGeocoder(client, url, id, key, language, cacheSize, addressFormat);
+ break;
case "mapmyindia":
- return new MapmyIndiaGeocoder(url, key, cacheSize, addressFormat);
+ geocoder = new MapmyIndiaGeocoder(client, url, key, cacheSize, addressFormat);
+ break;
case "tomtom":
- return new TomTomGeocoder(url, key, cacheSize, addressFormat);
+ geocoder = new TomTomGeocoder(client, url, key, cacheSize, addressFormat);
+ break;
case "positionstack":
- return new PositionStackGeocoder(key, cacheSize, addressFormat);
+ geocoder = new PositionStackGeocoder(client, key, cacheSize, addressFormat);
+ break;
case "mapbox":
- return new MapboxGeocoder(key, cacheSize, addressFormat);
+ geocoder = new MapboxGeocoder(client, key, cacheSize, addressFormat);
+ break;
case "maptiler":
- return new MapTilerGeocoder(key, cacheSize, addressFormat);
+ geocoder = new MapTilerGeocoder(client, key, cacheSize, addressFormat);
+ break;
+ case "geoapify":
+ geocoder = new GeoapifyGeocoder(client, key, language, cacheSize, addressFormat);
+ break;
default:
- return new GoogleGeocoder(key, language, cacheSize, addressFormat);
+ geocoder = new GoogleGeocoder(client, key, language, cacheSize, addressFormat);
+ break;
}
+ geocoder.setStatisticsManager(statisticsManager);
+ return geocoder;
}
return null;
}
@Singleton
@Provides
- public static GeolocationProvider provideGeolocationProvider(Config config) {
+ public static GeolocationProvider provideGeolocationProvider(Config config, Client client) {
if (config.getBoolean(Keys.GEOLOCATION_ENABLE)) {
String type = config.getString(Keys.GEOLOCATION_TYPE, "mozilla");
String url = config.getString(Keys.GEOLOCATION_URL);
String key = config.getString(Keys.GEOLOCATION_KEY);
switch (type) {
case "google":
- return new GoogleGeolocationProvider(key);
+ return new GoogleGeolocationProvider(client, key);
case "opencellid":
- return new OpenCellIdGeolocationProvider(url, key);
+ return new OpenCellIdGeolocationProvider(client, url, key);
case "unwired":
- return new UnwiredGeolocationProvider(url, key);
+ return new UnwiredGeolocationProvider(client, url, key);
default:
- return new MozillaGeolocationProvider(key);
+ return new MozillaGeolocationProvider(client, key);
}
}
return null;
@@ -227,14 +270,14 @@ public class MainModule extends AbstractModule {
@Singleton
@Provides
- public static SpeedLimitProvider provideSpeedLimitProvider(Config config) {
+ public static SpeedLimitProvider provideSpeedLimitProvider(Config config, Client client) {
if (config.getBoolean(Keys.SPEED_LIMIT_ENABLE)) {
String type = config.getString(Keys.SPEED_LIMIT_TYPE, "overpass");
String url = config.getString(Keys.SPEED_LIMIT_URL);
switch (type) {
case "overpass":
default:
- return new OverpassSpeedLimitProvider(url);
+ return new OverpassSpeedLimitProvider(client, url);
}
}
return null;
@@ -242,53 +285,11 @@ public class MainModule extends AbstractModule {
@Singleton
@Provides
- public static DistanceHandler provideDistanceHandler(Config config, IdentityManager identityManager) {
- return new DistanceHandler(config, identityManager);
- }
-
- @Singleton
- @Provides
- public static FilterHandler provideFilterHandler(Config config) {
- if (config.getBoolean(Keys.FILTER_ENABLE)) {
- return new FilterHandler(config);
- }
- return null;
- }
-
- @Singleton
- @Provides
- public static HemisphereHandler provideHemisphereHandler(Config config) {
- if (config.hasKey(Keys.LOCATION_LATITUDE_HEMISPHERE) || config.hasKey(Keys.LOCATION_LONGITUDE_HEMISPHERE)) {
- return new HemisphereHandler(config);
- }
- return null;
- }
-
- @Singleton
- @Provides
- public static RemoteAddressHandler provideRemoteAddressHandler(Config config) {
- if (config.getBoolean(Keys.PROCESSING_REMOTE_ADDRESS_ENABLE)) {
- return new RemoteAddressHandler();
- }
- return null;
- }
-
- @Singleton
- @Provides
- public static WebDataHandler provideWebDataHandler(
- Config config, IdentityManager identityManager, ObjectMapper objectMapper, Client client) {
- if (config.hasKey(Keys.FORWARD_URL)) {
- return new WebDataHandler(config, identityManager, objectMapper, client);
- }
- return null;
- }
-
- @Singleton
- @Provides
public static GeolocationHandler provideGeolocationHandler(
- Config config, @Nullable GeolocationProvider geolocationProvider, StatisticsManager statisticsManager) {
+ Config config, @Nullable GeolocationProvider geolocationProvider, CacheManager cacheManager,
+ StatisticsManager statisticsManager) {
if (geolocationProvider != null) {
- return new GeolocationHandler(config, geolocationProvider, statisticsManager);
+ return new GeolocationHandler(config, geolocationProvider, cacheManager, statisticsManager);
}
return null;
}
@@ -296,9 +297,9 @@ public class MainModule extends AbstractModule {
@Singleton
@Provides
public static GeocoderHandler provideGeocoderHandler(
- Config config, @Nullable Geocoder geocoder, IdentityManager identityManager) {
+ Config config, @Nullable Geocoder geocoder, CacheManager cacheManager) {
if (geocoder != null) {
- return new GeocoderHandler(config, geocoder, identityManager);
+ return new GeocoderHandler(config, geocoder, cacheManager);
}
return null;
}
@@ -314,130 +315,70 @@ public class MainModule extends AbstractModule {
@Singleton
@Provides
- public static MotionHandler provideMotionHandler(TripsConfig tripsConfig) {
- return new MotionHandler(tripsConfig.getSpeedThreshold());
- }
-
- @Singleton
- @Provides
- public static EngineHoursHandler provideEngineHoursHandler(Config config, IdentityManager identityManager) {
- if (config.getBoolean(Keys.PROCESSING_ENGINE_HOURS_ENABLE)) {
- return new EngineHoursHandler(identityManager);
+ public static BroadcastService provideBroadcastService(
+ Config config, ObjectMapper objectMapper) throws IOException {
+ if (config.hasKey(Keys.BROADCAST_ADDRESS)) {
+ return new MulticastBroadcastService(config, objectMapper);
}
- return null;
+ return new NullBroadcastService();
}
@Singleton
@Provides
- public static CopyAttributesHandler provideCopyAttributesHandler(Config config, IdentityManager identityManager) {
- if (config.getBoolean(Keys.PROCESSING_COPY_ATTRIBUTES_ENABLE)) {
- return new CopyAttributesHandler(identityManager);
- }
- return null;
- }
-
- @Singleton
- @Provides
- public static ComputedAttributesHandler provideComputedAttributesHandler(
- Config config, IdentityManager identityManager, AttributesManager attributesManager) {
- if (config.getBoolean(Keys.PROCESSING_COMPUTED_ATTRIBUTES_ENABLE)) {
- return new ComputedAttributesHandler(config, identityManager, attributesManager);
+ public static EventForwarder provideEventForwarder(Config config, Client client, ObjectMapper objectMapper) {
+ if (config.hasKey(Keys.EVENT_FORWARD_URL)) {
+ String forwardType = config.getString(Keys.EVENT_FORWARD_TYPE);
+ switch (forwardType) {
+ case "kafka":
+ return new EventForwarderKafka(config, objectMapper);
+ case "mqtt":
+ return new EventForwarderMqtt(config, objectMapper);
+ default:
+ return new EventForwarderJson(config, client);
+ }
}
return null;
}
@Singleton
@Provides
- public static TimeHandler provideTimeHandler(Config config) {
- if (config.hasKey(Keys.TIME_OVERRIDE)) {
- return new TimeHandler(config);
+ public static PositionForwarder providePositionForwarder(Config config, Client client, ObjectMapper objectMapper) {
+ if (config.hasKey(Keys.FORWARD_URL)) {
+ switch (config.getString(Keys.FORWARD_TYPE)) {
+ case "json":
+ return new PositionForwarderJson(config, client, objectMapper);
+ case "kafka":
+ return new PositionForwarderKafka(config, objectMapper);
+ default:
+ return new PositionForwarderUrl(config, client, objectMapper);
+ }
}
return null;
}
@Singleton
@Provides
- public static DefaultDataHandler provideDefaultDataHandler(@Nullable DataManager dataManager) {
- if (dataManager != null) {
- return new DefaultDataHandler(dataManager);
+ 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);
}
- return null;
- }
- @Singleton
- @Provides
- public static CommandResultEventHandler provideCommandResultEventHandler() {
- return new CommandResultEventHandler();
- }
-
- @Singleton
- @Provides
- public static OverspeedEventHandler provideOverspeedEventHandler(
- Config config, DeviceManager deviceManager, GeofenceManager geofenceManager) {
- return new OverspeedEventHandler(config, deviceManager, geofenceManager);
- }
-
- @Singleton
- @Provides
- public static BehaviorEventHandler provideBehaviorEventHandler(Config config, IdentityManager identityManager) {
- return new BehaviorEventHandler(config, identityManager);
- }
-
- @Singleton
- @Provides
- public static FuelDropEventHandler provideFuelDropEventHandler(IdentityManager identityManager) {
- return new FuelDropEventHandler(identityManager);
- }
-
- @Singleton
- @Provides
- public static MotionEventHandler provideMotionEventHandler(
- IdentityManager identityManager, DeviceManager deviceManager, TripsConfig tripsConfig) {
- return new MotionEventHandler(identityManager, deviceManager, tripsConfig);
- }
-
- @Singleton
- @Provides
- public static GeofenceEventHandler provideGeofenceEventHandler(
- IdentityManager identityManager, GeofenceManager geofenceManager, CalendarManager calendarManager,
- ConnectionManager connectionManager) {
- return new GeofenceEventHandler(identityManager, geofenceManager, calendarManager, connectionManager);
- }
-
- @Singleton
- @Provides
- public static AlertEventHandler provideAlertEventHandler(Config config, IdentityManager identityManager) {
- return new AlertEventHandler(config, identityManager);
- }
-
- @Singleton
- @Provides
- public static IgnitionEventHandler provideIgnitionEventHandler(IdentityManager identityManager) {
- return new IgnitionEventHandler(identityManager);
- }
-
- @Singleton
- @Provides
- public static MaintenanceEventHandler provideMaintenanceEventHandler(
- IdentityManager identityManager, MaintenancesManager maintenancesManager) {
- return new MaintenanceEventHandler(identityManager, maintenancesManager);
- }
-
- @Singleton
- @Provides
- public static DriverEventHandler provideDriverEventHandler(IdentityManager identityManager) {
- return new DriverEventHandler(identityManager);
- }
-
- @Singleton
- @Provides
- public static Timer provideTimer() {
- return GlobalTimer.getTimer();
- }
-
- @Override
- protected void configure() {
- binder().requireExplicitBindings();
+ VelocityEngine velocityEngine = new VelocityEngine();
+ velocityEngine.init(properties);
+ return velocityEngine;
}
}
diff --git a/src/main/java/org/traccar/PositionForwardingHandler.java b/src/main/java/org/traccar/PositionForwardingHandler.java
new file mode 100644
index 000000000..83f91e937
--- /dev/null
+++ b/src/main/java/org/traccar/PositionForwardingHandler.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar;
+
+import io.netty.channel.ChannelHandler;
+import io.netty.util.Timeout;
+import io.netty.util.Timer;
+import io.netty.util.TimerTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.forward.PositionData;
+import org.traccar.forward.PositionForwarder;
+import org.traccar.forward.ResultHandler;
+import org.traccar.model.Device;
+import org.traccar.model.Position;
+import org.traccar.session.cache.CacheManager;
+
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@Singleton
+@ChannelHandler.Sharable
+public class PositionForwardingHandler extends BaseDataHandler {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(PositionForwardingHandler.class);
+
+ private final CacheManager cacheManager;
+ private final Timer timer;
+
+ private final PositionForwarder positionForwarder;
+
+ private final boolean retryEnabled;
+ private final int retryDelay;
+ private final int retryCount;
+ private final int retryLimit;
+
+ private final AtomicInteger deliveryPending;
+
+ @Inject
+ public PositionForwardingHandler(
+ Config config, CacheManager cacheManager, Timer timer, @Nullable PositionForwarder positionForwarder) {
+
+ this.cacheManager = cacheManager;
+ this.timer = timer;
+ this.positionForwarder = positionForwarder;
+
+ this.retryEnabled = config.getBoolean(Keys.FORWARD_RETRY_ENABLE);
+ this.retryDelay = config.getInteger(Keys.FORWARD_RETRY_DELAY);
+ this.retryCount = config.getInteger(Keys.FORWARD_RETRY_COUNT);
+ this.retryLimit = config.getInteger(Keys.FORWARD_RETRY_LIMIT);
+
+ this.deliveryPending = new AtomicInteger();
+ }
+
+ class AsyncRequestAndCallback implements ResultHandler, TimerTask {
+
+ private final PositionData positionData;
+
+ private int retries = 0;
+
+ AsyncRequestAndCallback(PositionData positionData) {
+ this.positionData = positionData;
+ deliveryPending.incrementAndGet();
+ }
+
+ private void send() {
+ positionForwarder.forward(positionData, this);
+ }
+
+ private void retry(Throwable throwable) {
+ boolean scheduled = false;
+ try {
+ if (retryEnabled && deliveryPending.get() <= retryLimit && retries < retryCount) {
+ schedule();
+ scheduled = true;
+ }
+ } finally {
+ int pending = scheduled ? deliveryPending.get() : deliveryPending.decrementAndGet();
+ LOGGER.warn("Position forwarding failed: " + pending + " pending", throwable);
+ }
+ }
+
+ private void schedule() {
+ timer.newTimeout(this, retryDelay * (long) Math.pow(2, retries++), TimeUnit.MILLISECONDS);
+ }
+
+ @Override
+ public void onResult(boolean success, Throwable throwable) {
+ if (success) {
+ deliveryPending.decrementAndGet();
+ } else {
+ retry(throwable);
+ }
+ }
+
+ @Override
+ public void run(Timeout timeout) {
+ boolean sent = false;
+ try {
+ if (!timeout.isCancelled()) {
+ send();
+ sent = true;
+ }
+ } finally {
+ if (!sent) {
+ deliveryPending.decrementAndGet();
+ }
+ }
+ }
+ }
+
+ @Override
+ protected Position handlePosition(Position position) {
+ if (positionForwarder != null) {
+ PositionData positionData = new PositionData();
+ positionData.setPosition(position);
+ positionData.setDevice(cacheManager.getObject(Device.class, position.getDeviceId()));
+ new AsyncRequestAndCallback(positionData).send();
+ }
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/Protocol.java b/src/main/java/org/traccar/Protocol.java
index aea69b353..bc9c99557 100644
--- a/src/main/java/org/traccar/Protocol.java
+++ b/src/main/java/org/traccar/Protocol.java
@@ -25,7 +25,7 @@ public interface Protocol {
String getName();
- Collection<TrackerServer> getServerList();
+ Collection<TrackerConnector> getConnectorList();
Collection<String> getSupportedDataCommands();
diff --git a/src/main/java/org/traccar/ServerManager.java b/src/main/java/org/traccar/ServerManager.java
index 935a821aa..57afb01fd 100644
--- a/src/main/java/org/traccar/ServerManager.java
+++ b/src/main/java/org/traccar/ServerManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,95 +15,80 @@
*/
package org.traccar;
+import com.google.inject.Injector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.traccar.config.Config;
import org.traccar.config.Keys;
+import org.traccar.helper.ClassScanner;
-import java.io.File;
+import javax.inject.Inject;
+import javax.inject.Singleton;
import java.io.IOException;
import java.net.BindException;
-import java.net.URI;
+import java.net.ConnectException;
import java.net.URISyntaxException;
-import java.net.URL;
-import java.net.URLDecoder;
-import java.nio.charset.StandardCharsets;
-import java.util.Enumeration;
+import java.util.Arrays;
+import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
-public class ServerManager {
+@Singleton
+public class ServerManager implements LifecycleObject {
private static final Logger LOGGER = LoggerFactory.getLogger(ServerManager.class);
- private final List<TrackerServer> serverList = new LinkedList<>();
+ private final List<TrackerConnector> connectorList = new LinkedList<>();
private final Map<String, BaseProtocol> protocolList = new ConcurrentHashMap<>();
- private void loadPackage(String packageName) throws IOException, URISyntaxException, ReflectiveOperationException {
-
- List<String> names = new LinkedList<>();
- String packagePath = packageName.replace('.', '/');
- URL packageUrl = getClass().getClassLoader().getResource(packagePath);
-
- if (packageUrl.getProtocol().equals("jar")) {
- String jarFileName = URLDecoder.decode(packageUrl.getFile(), StandardCharsets.UTF_8.name());
- try (JarFile jf = new JarFile(jarFileName.substring(5, jarFileName.indexOf("!")))) {
- Enumeration<JarEntry> jarEntries = jf.entries();
- while (jarEntries.hasMoreElements()) {
- String entryName = jarEntries.nextElement().getName();
- if (entryName.startsWith(packagePath) && entryName.length() > packagePath.length() + 5) {
- names.add(entryName.substring(packagePath.length() + 1, entryName.lastIndexOf('.')));
- }
- }
- }
- } else {
- File folder = new File(new URI(packageUrl.toString()));
- File[] files = folder.listFiles();
- if (files != null) {
- for (File actual: files) {
- String entryName = actual.getName();
- names.add(entryName.substring(0, entryName.lastIndexOf('.')));
- }
- }
+ @Inject
+ public ServerManager(
+ Injector injector, Config config) throws IOException, URISyntaxException, ReflectiveOperationException {
+ Set<String> enabledProtocols = null;
+ if (config.hasKey(Keys.PROTOCOLS_ENABLE)) {
+ enabledProtocols = new HashSet<>(Arrays.asList(config.getString(Keys.PROTOCOLS_ENABLE).split("[, ]")));
}
-
- for (String name : names) {
- Class<?> protocolClass = Class.forName(packageName + '.' + name);
- if (BaseProtocol.class.isAssignableFrom(protocolClass) && Context.getConfig().hasKey(
- Keys.PROTOCOL_PORT.withPrefix(BaseProtocol.nameFromClass(protocolClass)))) {
- BaseProtocol protocol = (BaseProtocol) protocolClass.getDeclaredConstructor().newInstance();
- serverList.addAll(protocol.getServerList());
- protocolList.put(protocol.getName(), protocol);
+ for (Class<?> protocolClass : ClassScanner.findSubclasses(BaseProtocol.class, "org.traccar.protocol")) {
+ String protocolName = BaseProtocol.nameFromClass(protocolClass);
+ if (enabledProtocols == null || enabledProtocols.contains(protocolName)) {
+ if (config.hasKey(Keys.PROTOCOL_PORT.withPrefix(protocolName))) {
+ BaseProtocol protocol = (BaseProtocol) injector.getInstance(protocolClass);
+ connectorList.addAll(protocol.getConnectorList());
+ protocolList.put(protocol.getName(), protocol);
+ }
}
}
}
- public ServerManager() throws IOException, URISyntaxException, ReflectiveOperationException {
- loadPackage("org.traccar.protocol");
- }
-
public BaseProtocol getProtocol(String name) {
return protocolList.get(name);
}
+ @Override
public void start() throws Exception {
- for (TrackerServer server: serverList) {
+ for (TrackerConnector connector: connectorList) {
try {
- server.start();
+ connector.start();
} catch (BindException e) {
- LOGGER.warn("Port {} is disabled due to conflict", server.getPort());
+ LOGGER.warn("Port disabled due to conflict", e);
+ } catch (ConnectException e) {
+ LOGGER.warn("Connection failed", e);
}
}
}
- public void stop() {
- for (TrackerServer server: serverList) {
- server.stop();
+ @Override
+ public void stop() throws Exception {
+ try {
+ for (TrackerConnector connector : connectorList) {
+ connector.stop();
+ }
+ } finally {
+ GlobalTimer.release();
}
- GlobalTimer.release();
}
}
diff --git a/src/main/java/org/traccar/TrackerClient.java b/src/main/java/org/traccar/TrackerClient.java
new file mode 100644
index 000000000..2d6b227da
--- /dev/null
+++ b/src/main/java/org/traccar/TrackerClient.java
@@ -0,0 +1,125 @@
+/*
+ * 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;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.group.ChannelGroup;
+import io.netty.channel.group.DefaultChannelGroup;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.ssl.SslHandler;
+import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.GenericFutureListener;
+import io.netty.util.concurrent.GlobalEventExecutor;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import java.util.concurrent.TimeUnit;
+
+public abstract class TrackerClient implements TrackerConnector {
+
+ private final boolean secure;
+ private final long interval;
+
+ private final Bootstrap bootstrap;
+
+ private final int port;
+ private final String address;
+ private final String[] devices;
+
+ private final ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
+
+ @Override
+ public boolean isDatagram() {
+ return false;
+ }
+
+ @Override
+ public boolean isSecure() {
+ return secure;
+ }
+
+ public TrackerClient(Config config, String protocol) {
+ secure = config.getBoolean(Keys.PROTOCOL_SSL.withPrefix(protocol));
+ interval = config.getLong(Keys.PROTOCOL_INTERVAL.withPrefix(protocol));
+ address = config.getString(Keys.PROTOCOL_ADDRESS.withPrefix(protocol));
+ port = config.getInteger(Keys.PROTOCOL_PORT.withPrefix(protocol), secure ? 443 : 80);
+ devices = config.getString(Keys.PROTOCOL_DEVICES.withPrefix(protocol)).split("[, ]");
+
+ BasePipelineFactory pipelineFactory = new BasePipelineFactory(this, config, protocol) {
+ @Override
+ protected void addTransportHandlers(PipelineBuilder pipeline) {
+ try {
+ if (isSecure()) {
+ SSLEngine engine = SSLContext.getDefault().createSSLEngine();
+ engine.setUseClientMode(true);
+ pipeline.addLast(new SslHandler(engine));
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ try {
+ TrackerClient.this.addProtocolHandlers(pipeline, config);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+
+ bootstrap = new Bootstrap()
+ .group(EventLoopGroupFactory.getWorkerGroup())
+ .channel(NioSocketChannel.class)
+ .handler(pipelineFactory);
+ }
+
+ protected abstract void addProtocolHandlers(PipelineBuilder pipeline, Config config) throws Exception;
+
+ public String[] getDevices() {
+ return devices;
+ }
+
+ @Override
+ public ChannelGroup getChannelGroup() {
+ return channelGroup;
+ }
+
+ @Override
+ public void start() throws Exception {
+ bootstrap.connect(address, port)
+ .syncUninterruptibly().channel().closeFuture().addListener(new GenericFutureListener<>() {
+ @Override
+ public void operationComplete(Future<? super Void> future) {
+ if (interval > 0) {
+ GlobalEventExecutor.INSTANCE.schedule(() -> {
+ bootstrap.connect(address, port)
+ .syncUninterruptibly().channel().closeFuture().addListener(this);
+ }, interval, TimeUnit.SECONDS);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void stop() {
+ channelGroup.close().awaitUninterruptibly();
+ }
+
+}
diff --git a/src/main/java/org/traccar/database/ManagableObjects.java b/src/main/java/org/traccar/TrackerConnector.java
index ec9549493..fc6e93399 100644
--- a/src/main/java/org/traccar/database/ManagableObjects.java
+++ b/src/main/java/org/traccar/TrackerConnector.java
@@ -1,6 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
- * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
+ * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.database;
+package org.traccar;
-import java.util.Set;
+import io.netty.channel.group.ChannelGroup;
-public interface ManagableObjects {
+public interface TrackerConnector extends LifecycleObject {
- Set<Long> getUserItems(long userId);
+ boolean isDatagram();
- Set<Long> getManagedItems(long userId);
+ boolean isSecure();
+
+ ChannelGroup getChannelGroup();
}
diff --git a/src/main/java/org/traccar/TrackerServer.java b/src/main/java/org/traccar/TrackerServer.java
index 59ba123e2..0e0837cfb 100644
--- a/src/main/java/org/traccar/TrackerServer.java
+++ b/src/main/java/org/traccar/TrackerServer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,78 +23,92 @@ import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.handler.ssl.SslHandler;
import io.netty.util.concurrent.GlobalEventExecutor;
+import org.traccar.config.Config;
import org.traccar.config.Keys;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
import java.net.InetSocketAddress;
-public abstract class TrackerServer {
+public abstract class TrackerServer implements TrackerConnector {
private final boolean datagram;
+ private final boolean secure;
+
+ @SuppressWarnings("rawtypes")
private final AbstractBootstrap bootstrap;
+ private final int port;
+ private final String address;
+
+ private final ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
+
+ @Override
public boolean isDatagram() {
return datagram;
}
- public TrackerServer(boolean datagram, String protocol) {
- this.datagram = datagram;
+ @Override
+ public boolean isSecure() {
+ return secure;
+ }
- address = Context.getConfig().getString(Keys.PROTOCOL_ADDRESS.withPrefix(protocol));
- port = Context.getConfig().getInteger(Keys.PROTOCOL_PORT.withPrefix(protocol));
+ public TrackerServer(Config config, String protocol, boolean datagram) {
+ secure = config.getBoolean(Keys.PROTOCOL_SSL.withPrefix(protocol));
+ address = config.getString(Keys.PROTOCOL_ADDRESS.withPrefix(protocol));
+ port = config.getInteger(Keys.PROTOCOL_PORT.withPrefix(protocol));
+
+ BasePipelineFactory pipelineFactory = new BasePipelineFactory(this, config, protocol) {
+ @Override
+ protected void addTransportHandlers(PipelineBuilder pipeline) {
+ try {
+ if (isSecure()) {
+ SSLEngine engine = SSLContext.getDefault().createSSLEngine();
+ pipeline.addLast(new SslHandler(engine));
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
- BasePipelineFactory pipelineFactory = new BasePipelineFactory(this, protocol) {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline) {
- TrackerServer.this.addProtocolHandlers(pipeline);
+ TrackerServer.this.addProtocolHandlers(pipeline, config);
}
};
+ this.datagram = datagram;
if (datagram) {
-
- this.bootstrap = new Bootstrap()
+ bootstrap = new Bootstrap()
.group(EventLoopGroupFactory.getWorkerGroup())
.channel(NioDatagramChannel.class)
.handler(pipelineFactory);
-
} else {
-
- this.bootstrap = new ServerBootstrap()
+ bootstrap = new ServerBootstrap()
.group(EventLoopGroupFactory.getBossGroup(), EventLoopGroupFactory.getWorkerGroup())
.channel(NioServerSocketChannel.class)
.childHandler(pipelineFactory);
-
}
}
- protected abstract void addProtocolHandlers(PipelineBuilder pipeline);
-
- private int port;
+ protected abstract void addProtocolHandlers(PipelineBuilder pipeline, Config config);
public int getPort() {
return port;
}
- public void setPort(int port) {
- this.port = port;
- }
-
- private String address;
-
public String getAddress() {
return address;
}
- public void setAddress(String address) {
- this.address = address;
- }
-
- private final ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
-
+ @Override
public ChannelGroup getChannelGroup() {
return channelGroup;
}
+ @Override
public void start() throws Exception {
InetSocketAddress endpoint;
if (address == null) {
@@ -103,12 +117,13 @@ public abstract class TrackerServer {
endpoint = new InetSocketAddress(address, port);
}
- Channel channel = bootstrap.bind(endpoint).sync().channel();
+ Channel channel = bootstrap.bind(endpoint).syncUninterruptibly().channel();
if (channel != null) {
getChannelGroup().add(channel);
}
}
+ @Override
public void stop() {
channelGroup.close().awaitUninterruptibly();
}
diff --git a/src/main/java/org/traccar/WebDataHandler.java b/src/main/java/org/traccar/WebDataHandler.java
deleted file mode 100644
index 678096d34..000000000
--- a/src/main/java/org/traccar/WebDataHandler.java
+++ /dev/null
@@ -1,310 +0,0 @@
-/*
- * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import io.netty.channel.ChannelHandler;
-import io.netty.util.Timer;
-import io.netty.util.Timeout;
-import io.netty.util.TimerTask;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.traccar.config.Config;
-import org.traccar.config.Keys;
-import org.traccar.database.IdentityManager;
-import org.traccar.helper.Checksum;
-import org.traccar.model.Device;
-import org.traccar.model.Position;
-import org.traccar.model.Group;
-
-import javax.inject.Inject;
-import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.client.Client;
-import javax.ws.rs.client.Entity;
-import javax.ws.rs.client.Invocation;
-import javax.ws.rs.client.InvocationCallback;
-import java.util.HashMap;
-import java.util.Map;
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
-import java.util.Calendar;
-import java.util.Formatter;
-import java.util.Locale;
-import java.util.TimeZone;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-@ChannelHandler.Sharable
-public class WebDataHandler extends BaseDataHandler {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(WebDataHandler.class);
-
- private static final String KEY_POSITION = "position";
- private static final String KEY_DEVICE = "device";
-
- private final IdentityManager identityManager;
- private final ObjectMapper objectMapper;
- private final Client client;
-
- private final String url;
- private final String header;
- private final boolean json;
- private final boolean urlVariables;
-
- private final boolean retryEnabled;
- private final int retryDelay;
- private final int retryCount;
- private final int retryLimit;
-
- private final AtomicInteger deliveryPending;
-
- @Inject
- public WebDataHandler(
- Config config, IdentityManager identityManager, ObjectMapper objectMapper, Client client) {
-
- this.identityManager = identityManager;
- this.objectMapper = objectMapper;
- this.client = client;
- this.url = config.getString(Keys.FORWARD_URL);
- this.header = config.getString(Keys.FORWARD_HEADER);
- this.json = config.getBoolean(Keys.FORWARD_JSON);
- this.urlVariables = config.getBoolean(Keys.FORWARD_URL_VARIABLES);
-
- this.retryEnabled = config.getBoolean(Keys.FORWARD_RETRY_ENABLE);
- this.retryDelay = config.getInteger(Keys.FORWARD_RETRY_DELAY, 100);
- this.retryCount = config.getInteger(Keys.FORWARD_RETRY_COUNT, 10);
- this.retryLimit = config.getInteger(Keys.FORWARD_RETRY_LIMIT, 100);
-
- this.deliveryPending = new AtomicInteger(0);
- }
-
- private static String formatSentence(Position position) {
-
- StringBuilder s = new StringBuilder("$GPRMC,");
-
- try (Formatter f = new Formatter(s, Locale.ENGLISH)) {
-
- Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"), Locale.ENGLISH);
- calendar.setTimeInMillis(position.getFixTime().getTime());
-
- f.format("%1$tH%1$tM%1$tS.%1$tL,A,", calendar);
-
- double lat = position.getLatitude();
- double lon = position.getLongitude();
-
- f.format("%02d%07.4f,%c,", (int) Math.abs(lat), Math.abs(lat) % 1 * 60, lat < 0 ? 'S' : 'N');
- f.format("%03d%07.4f,%c,", (int) Math.abs(lon), Math.abs(lon) % 1 * 60, lon < 0 ? 'W' : 'E');
-
- f.format("%.2f,%.2f,", position.getSpeed(), position.getCourse());
- f.format("%1$td%1$tm%1$ty,,", calendar);
- }
-
- s.append(Checksum.nmea(s.substring(1)));
-
- return s.toString();
- }
-
- private String calculateStatus(Position position) {
- if (position.getAttributes().containsKey(Position.KEY_ALARM)) {
- return "0xF841"; // STATUS_PANIC_ON
- } else if (position.getSpeed() < 1.0) {
- return "0xF020"; // STATUS_LOCATION
- } else {
- return "0xF11C"; // STATUS_MOTION_MOVING
- }
- }
-
- public String formatRequest(Position position) throws UnsupportedEncodingException, JsonProcessingException {
-
- Device device = identityManager.getById(position.getDeviceId());
-
- String request = url
- .replace("{name}", URLEncoder.encode(device.getName(), StandardCharsets.UTF_8.name()))
- .replace("{uniqueId}", device.getUniqueId())
- .replace("{status}", device.getStatus())
- .replace("{deviceId}", String.valueOf(position.getDeviceId()))
- .replace("{protocol}", String.valueOf(position.getProtocol()))
- .replace("{deviceTime}", String.valueOf(position.getDeviceTime().getTime()))
- .replace("{fixTime}", String.valueOf(position.getFixTime().getTime()))
- .replace("{valid}", String.valueOf(position.getValid()))
- .replace("{latitude}", String.valueOf(position.getLatitude()))
- .replace("{longitude}", String.valueOf(position.getLongitude()))
- .replace("{altitude}", String.valueOf(position.getAltitude()))
- .replace("{speed}", String.valueOf(position.getSpeed()))
- .replace("{course}", String.valueOf(position.getCourse()))
- .replace("{accuracy}", String.valueOf(position.getAccuracy()))
- .replace("{statusCode}", calculateStatus(position));
-
- if (position.getAddress() != null) {
- request = request.replace(
- "{address}", URLEncoder.encode(position.getAddress(), StandardCharsets.UTF_8.name()));
- }
-
- if (request.contains("{attributes}")) {
- String attributes = objectMapper.writeValueAsString(position.getAttributes());
- request = request.replace(
- "{attributes}", URLEncoder.encode(attributes, StandardCharsets.UTF_8.name()));
- }
-
- if (request.contains("{gprmc}")) {
- request = request.replace("{gprmc}", formatSentence(position));
- }
-
- if (request.contains("{group}")) {
- String deviceGroupName = "";
- if (device.getGroupId() != 0) {
- Group group = Context.getGroupsManager().getById(device.getGroupId());
- if (group != null) {
- deviceGroupName = group.getName();
- }
- }
-
- request = request.replace("{group}", URLEncoder.encode(deviceGroupName, StandardCharsets.UTF_8.name()));
- }
-
- return request;
- }
-
- class AsyncRequestAndCallback implements InvocationCallback<Response>, TimerTask {
-
- private int retries = 0;
- private Map<String, Object> payload;
- private final Invocation.Builder requestBuilder;
- private MediaType mediaType = MediaType.APPLICATION_JSON_TYPE;
-
- AsyncRequestAndCallback(Position position) {
-
- String formattedUrl;
- try {
- formattedUrl = json && !urlVariables ? url : formatRequest(position);
- } catch (UnsupportedEncodingException | JsonProcessingException e) {
- throw new RuntimeException("Forwarding formatting error", e);
- }
-
- requestBuilder = client.target(formattedUrl).request();
- if (header != null && !header.isEmpty()) {
- for (String line: header.split("\\r?\\n")) {
- String[] values = line.split(":", 2);
- String headerName = values[0].trim();
- String headerValue = values[1].trim();
- if (headerName.equals(HttpHeaders.CONTENT_TYPE)) {
- mediaType = MediaType.valueOf(headerValue);
- } else {
- requestBuilder.header(headerName, headerValue);
- }
- }
- }
-
- if (json) {
- payload = prepareJsonPayload(position);
- }
-
- deliveryPending.incrementAndGet();
- }
-
- private void send() {
- LOGGER.debug("Position forwarding initiated");
- if (json) {
- try {
- Entity<String> entity = Entity.entity(objectMapper.writeValueAsString(payload), mediaType);
- requestBuilder.async().post(entity, this);
- } catch (JsonProcessingException e) {
- throw new RuntimeException("Failed to serialize location to json", e);
- }
- } else {
- requestBuilder.async().get(this);
- }
- }
-
- private void retry(Throwable throwable) {
- boolean scheduled = false;
- try {
- if (retryEnabled && deliveryPending.get() <= retryLimit && retries < retryCount) {
- schedule();
- scheduled = true;
- }
- } finally {
- int pending = scheduled ? deliveryPending.get() : deliveryPending.decrementAndGet();
- LOGGER.warn("Position forwarding failed: " + pending + " pending", throwable);
- }
- }
-
- private void schedule() {
- Main.getInjector().getInstance(Timer.class).newTimeout(
- this, retryDelay * (long) Math.pow(2, retries++), TimeUnit.MILLISECONDS);
- }
-
- @Override
- public void completed(Response response) {
- if (response.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL) {
- deliveryPending.decrementAndGet();
- LOGGER.debug("Position forwarding succeeded");
- } else {
- retry(new RuntimeException("Status code 2xx expected"));
- }
- }
-
- @Override
- public void failed(Throwable throwable) {
- retry(throwable);
- }
-
- @Override
- public void run(Timeout timeout) {
- boolean sent = false;
- try {
- if (!timeout.isCancelled()) {
- send();
- sent = true;
- }
- } finally {
- if (!sent) {
- deliveryPending.decrementAndGet();
- }
- }
- }
-
- }
-
- @Override
- protected Position handlePosition(Position position) {
-
- AsyncRequestAndCallback request = new AsyncRequestAndCallback(position);
- request.send();
-
- return position;
- }
-
- private Map<String, Object> prepareJsonPayload(Position position) {
-
- Map<String, Object> data = new HashMap<>();
- Device device = identityManager.getById(position.getDeviceId());
-
- data.put(KEY_POSITION, position);
-
- if (device != null) {
- data.put(KEY_DEVICE, device);
- }
-
- return data;
- }
-
-}
diff --git a/src/main/java/org/traccar/api/AsyncSocket.java b/src/main/java/org/traccar/api/AsyncSocket.java
index b1853822d..5fc4b4412 100644
--- a/src/main/java/org/traccar/api/AsyncSocket.java
+++ b/src/main/java/org/traccar/api/AsyncSocket.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,15 +16,18 @@
package org.traccar.api;
import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.traccar.Context;
-import org.traccar.database.ConnectionManager;
+import org.traccar.helper.model.PositionUtil;
+import org.traccar.session.ConnectionManager;
import org.traccar.model.Device;
import org.traccar.model.Event;
import org.traccar.model.Position;
+import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
import java.util.Collection;
import java.util.Collections;
@@ -39,9 +42,15 @@ public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.U
private static final String KEY_POSITIONS = "positions";
private static final String KEY_EVENTS = "events";
+ private final ObjectMapper objectMapper;
+ private final ConnectionManager connectionManager;
+ private final Storage storage;
private final long userId;
- public AsyncSocket(long userId) {
+ public AsyncSocket(ObjectMapper objectMapper, ConnectionManager connectionManager, Storage storage, long userId) {
+ this.objectMapper = objectMapper;
+ this.connectionManager = connectionManager;
+ this.storage = storage;
this.userId = userId;
}
@@ -49,18 +58,21 @@ public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.U
public void onWebSocketConnect(Session session) {
super.onWebSocketConnect(session);
- Map<String, Collection<?>> data = new HashMap<>();
- data.put(KEY_POSITIONS, Context.getDeviceManager().getInitialState(userId));
- sendData(data);
-
- Context.getConnectionManager().addListener(userId, this);
+ try {
+ Map<String, Collection<?>> data = new HashMap<>();
+ data.put(KEY_POSITIONS, PositionUtil.getLatestPositions(storage, userId));
+ sendData(data);
+ connectionManager.addListener(userId, this);
+ } catch (StorageException e) {
+ throw new RuntimeException(e);
+ }
}
@Override
public void onWebSocketClose(int statusCode, String reason) {
super.onWebSocketClose(statusCode, reason);
- Context.getConnectionManager().removeListener(userId, this);
+ connectionManager.removeListener(userId, this);
}
@Override
@@ -92,7 +104,7 @@ public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.U
private void sendData(Map<String, Collection<?>> data) {
if (isConnected()) {
try {
- getRemote().sendString(Context.getObjectMapper().writeValueAsString(data), null);
+ getRemote().sendString(objectMapper.writeValueAsString(data), null);
} catch (JsonProcessingException e) {
LOGGER.warn("Socket JSON formatting error", e);
}
diff --git a/src/main/java/org/traccar/api/AsyncSocketServlet.java b/src/main/java/org/traccar/api/AsyncSocketServlet.java
index a964ead10..91a745eeb 100644
--- a/src/main/java/org/traccar/api/AsyncSocketServlet.java
+++ b/src/main/java/org/traccar/api/AsyncSocketServlet.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,27 +15,48 @@
*/
package org.traccar.api;
+import com.fasterxml.jackson.databind.ObjectMapper;
import org.eclipse.jetty.websocket.server.JettyWebSocketServlet;
import org.eclipse.jetty.websocket.server.JettyWebSocketServletFactory;
-import org.traccar.Context;
import org.traccar.api.resource.SessionResource;
+import org.traccar.config.Config;
import org.traccar.config.Keys;
+import org.traccar.session.ConnectionManager;
+import org.traccar.storage.Storage;
+import javax.inject.Inject;
+import javax.inject.Singleton;
import javax.servlet.http.HttpSession;
import java.time.Duration;
+@Singleton
public class AsyncSocketServlet extends JettyWebSocketServlet {
+ private final Config config;
+ private final ObjectMapper objectMapper;
+ private final ConnectionManager connectionManager;
+ private final Storage storage;
+
+ @Inject
+ public AsyncSocketServlet(
+ Config config, ObjectMapper objectMapper, ConnectionManager connectionManager, Storage storage) {
+ this.config = config;
+ this.objectMapper = objectMapper;
+ this.connectionManager = connectionManager;
+ this.storage = storage;
+ }
+
@Override
public void configure(JettyWebSocketServletFactory factory) {
- factory.setIdleTimeout(Duration.ofMillis(Context.getConfig().getLong(Keys.WEB_TIMEOUT)));
+ factory.setIdleTimeout(Duration.ofMillis(config.getLong(Keys.WEB_TIMEOUT)));
factory.setCreator((req, resp) -> {
if (req.getSession() != null) {
- long userId = (Long) ((HttpSession) req.getSession()).getAttribute(SessionResource.USER_ID_KEY);
- return new AsyncSocket(userId);
- } else {
- return null;
+ Long userId = (Long) ((HttpSession) req.getSession()).getAttribute(SessionResource.USER_ID_KEY);
+ if (userId != null) {
+ return new AsyncSocket(objectMapper, connectionManager, storage, userId);
+ }
}
+ return null;
});
}
diff --git a/src/main/java/org/traccar/api/BaseObjectResource.java b/src/main/java/org/traccar/api/BaseObjectResource.java
index 71f3939cb..904781e54 100644
--- a/src/main/java/org/traccar/api/BaseObjectResource.java
+++ b/src/main/java/org/traccar/api/BaseObjectResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2017 - 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,19 @@
*/
package org.traccar.api;
-import java.sql.SQLException;
-import java.util.Set;
-
+import org.traccar.helper.LogAction;
+import org.traccar.model.BaseModel;
+import org.traccar.model.Group;
+import org.traccar.model.Permission;
+import org.traccar.model.User;
+import org.traccar.session.ConnectionManager;
+import org.traccar.session.cache.CacheManager;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Request;
+
+import javax.inject.Inject;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
@@ -27,58 +37,26 @@ import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;
-import org.traccar.Context;
-import org.traccar.database.BaseObjectManager;
-import org.traccar.database.ExtendedObjectManager;
-import org.traccar.database.ManagableObjects;
-import org.traccar.database.SimpleObjectManager;
-import org.traccar.helper.LogAction;
-import org.traccar.model.BaseModel;
-import org.traccar.model.Calendar;
-import org.traccar.model.Command;
-import org.traccar.model.Device;
-import org.traccar.model.Group;
-import org.traccar.model.GroupedModel;
-import org.traccar.model.ScheduledModel;
-import org.traccar.model.User;
-
public abstract class BaseObjectResource<T extends BaseModel> extends BaseResource {
- private final Class<T> baseClass;
+ @Inject
+ private CacheManager cacheManager;
- public BaseObjectResource(Class<T> baseClass) {
- this.baseClass = baseClass;
- }
+ @Inject
+ private ConnectionManager connectionManager;
- protected final Class<T> getBaseClass() {
- return baseClass;
- }
+ protected final Class<T> baseClass;
- protected final Set<Long> getSimpleManagerItems(BaseObjectManager<T> manager, boolean all, long userId) {
- Set<Long> result;
- if (all) {
- if (Context.getPermissionsManager().getUserAdmin(getUserId())) {
- result = manager.getAllItems();
- } else {
- Context.getPermissionsManager().checkManager(getUserId());
- result = ((ManagableObjects) manager).getManagedItems(getUserId());
- }
- } else {
- if (userId == 0) {
- userId = getUserId();
- }
- Context.getPermissionsManager().checkUser(getUserId(), userId);
- result = ((ManagableObjects) manager).getUserItems(userId);
- }
- return result;
+ public BaseObjectResource(Class<T> baseClass) {
+ this.baseClass = baseClass;
}
@Path("{id}")
@GET
- public Response getSingle(@PathParam("id") long id) throws SQLException {
- Context.getPermissionsManager().checkPermission(baseClass, getUserId(), id);
- BaseObjectManager<T> manager = Context.getManager(baseClass);
- T entity = manager.getById(id);
+ public Response getSingle(@PathParam("id") long id) throws StorageException {
+ permissionsService.checkPermission(baseClass, getUserId(), id);
+ T entity = storage.getObject(baseClass, new Request(
+ new Columns.All(), new Condition.Equals("id", id)));
if (entity != null) {
return Response.ok(entity).build();
} else {
@@ -87,103 +65,64 @@ public abstract class BaseObjectResource<T extends BaseModel> extends BaseResour
}
@POST
- public Response add(T entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- if (baseClass.equals(Device.class)) {
- Context.getPermissionsManager().checkDeviceReadonly(getUserId());
- Context.getPermissionsManager().checkDeviceLimit(getUserId());
- } else if (baseClass.equals(Command.class)) {
- Context.getPermissionsManager().checkLimitCommands(getUserId());
- } else if (entity instanceof GroupedModel && ((GroupedModel) entity).getGroupId() != 0) {
- Context.getPermissionsManager().checkPermission(
- Group.class, getUserId(), ((GroupedModel) entity).getGroupId());
- } else if (entity instanceof ScheduledModel && ((ScheduledModel) entity).getCalendarId() != 0) {
- Context.getPermissionsManager().checkPermission(
- Calendar.class, getUserId(), ((ScheduledModel) entity).getCalendarId());
- }
+ public Response add(T entity) throws StorageException {
+ permissionsService.checkEdit(getUserId(), entity, true);
- BaseObjectManager<T> manager = Context.getManager(baseClass);
- manager.addItem(entity);
+ entity.setId(storage.addObject(entity, new Request(new Columns.Exclude("id"))));
LogAction.create(getUserId(), entity);
-
- Context.getDataManager().linkObject(User.class, getUserId(), baseClass, entity.getId(), true);
+ 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 (manager instanceof SimpleObjectManager) {
- ((SimpleObjectManager<T>) manager).refreshUserItems();
- } else if (baseClass.equals(Group.class) || baseClass.equals(Device.class)) {
- Context.getPermissionsManager().refreshDeviceAndGroupPermissions();
- Context.getPermissionsManager().refreshAllExtendedPermissions();
- }
return Response.ok(entity).build();
}
@Path("{id}")
@PUT
- public Response update(T entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- if (baseClass.equals(Device.class)) {
- Context.getPermissionsManager().checkDeviceReadonly(getUserId());
- } else if (baseClass.equals(User.class)) {
- User before = Context.getPermissionsManager().getUser(entity.getId());
- Context.getPermissionsManager().checkUserUpdate(getUserId(), before, (User) entity);
- } else if (baseClass.equals(Command.class)) {
- Context.getPermissionsManager().checkLimitCommands(getUserId());
- } else if (entity instanceof GroupedModel && ((GroupedModel) entity).getGroupId() != 0) {
- Context.getPermissionsManager().checkPermission(
- Group.class, getUserId(), ((GroupedModel) entity).getGroupId());
- } else if (entity instanceof ScheduledModel && ((ScheduledModel) entity).getCalendarId() != 0) {
- Context.getPermissionsManager().checkPermission(
- Calendar.class, getUserId(), ((ScheduledModel) entity).getCalendarId());
+ public Response update(T entity) throws StorageException {
+ permissionsService.checkEdit(getUserId(), entity, false);
+ permissionsService.checkPermission(baseClass, getUserId(), entity.getId());
+
+ if (entity instanceof User) {
+ User before = storage.getObject(User.class, new Request(
+ new Columns.All(), new Condition.Equals("id", entity.getId())));
+ permissionsService.checkUserUpdate(getUserId(), before, (User) entity);
+ } else if (entity instanceof Group) {
+ Group group = (Group) entity;
+ if (group.getId() == group.getGroupId()) {
+ throw new IllegalArgumentException("Cycle in group hierarchy");
+ }
}
- Context.getPermissionsManager().checkPermission(baseClass, getUserId(), entity.getId());
- Context.getManager(baseClass).updateItem(entity);
+ storage.updateObject(entity, new Request(
+ new Columns.Exclude("id"),
+ new Condition.Equals("id", entity.getId())));
+ if (entity instanceof User) {
+ User user = (User) entity;
+ if (user.getHashedPassword() != null) {
+ storage.updateObject(entity, new Request(
+ new Columns.Include("hashedPassword", "salt"),
+ new Condition.Equals("id", entity.getId())));
+ }
+ }
+ cacheManager.updateOrInvalidate(true, entity);
LogAction.edit(getUserId(), entity);
- if (baseClass.equals(Group.class) || baseClass.equals(Device.class)) {
- Context.getPermissionsManager().refreshDeviceAndGroupPermissions();
- Context.getPermissionsManager().refreshAllExtendedPermissions();
- }
return Response.ok(entity).build();
}
@Path("{id}")
@DELETE
- public Response remove(@PathParam("id") long id) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- if (baseClass.equals(Device.class)) {
- Context.getPermissionsManager().checkDeviceReadonly(getUserId());
- } else if (baseClass.equals(Command.class)) {
- Context.getPermissionsManager().checkLimitCommands(getUserId());
- }
- Context.getPermissionsManager().checkPermission(baseClass, getUserId(), id);
+ public Response remove(@PathParam("id") long id) throws StorageException {
+ permissionsService.checkEdit(getUserId(), baseClass, false);
+ permissionsService.checkPermission(baseClass, getUserId(), id);
+
+ storage.removeObject(baseClass, new Request(new Condition.Equals("id", id)));
+ cacheManager.invalidate(baseClass, id);
- BaseObjectManager<T> manager = Context.getManager(baseClass);
- manager.removeItem(id);
LogAction.remove(getUserId(), baseClass, id);
- if (manager instanceof SimpleObjectManager) {
- ((SimpleObjectManager<T>) manager).refreshUserItems();
- if (manager instanceof ExtendedObjectManager) {
- ((ExtendedObjectManager<T>) manager).refreshExtendedPermissions();
- }
- }
- if (baseClass.equals(Group.class) || baseClass.equals(Device.class) || baseClass.equals(User.class)) {
- if (baseClass.equals(Group.class)) {
- Context.getGroupsManager().refreshItems();
- Context.getDeviceManager().updateDeviceCache(true);
- }
- Context.getPermissionsManager().refreshDeviceAndGroupPermissions();
- if (baseClass.equals(User.class)) {
- Context.getPermissionsManager().refreshAllUsersPermissions();
- } else {
- Context.getPermissionsManager().refreshAllExtendedPermissions();
- }
- } else if (baseClass.equals(Calendar.class)) {
- Context.getGeofenceManager().refreshItems();
- Context.getNotificationManager().refreshItems();
- }
return Response.noContent().build();
}
diff --git a/src/main/java/org/traccar/api/BaseResource.java b/src/main/java/org/traccar/api/BaseResource.java
index cc272df9c..33abe73fa 100644
--- a/src/main/java/org/traccar/api/BaseResource.java
+++ b/src/main/java/org/traccar/api/BaseResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,13 +15,25 @@
*/
package org.traccar.api;
+import org.traccar.api.security.PermissionsService;
+import org.traccar.api.security.UserPrincipal;
+import org.traccar.storage.Storage;
+
+import javax.inject.Inject;
+import javax.ws.rs.core.Context;
import javax.ws.rs.core.SecurityContext;
public class BaseResource {
- @javax.ws.rs.core.Context
+ @Context
private SecurityContext securityContext;
+ @Inject
+ protected Storage storage;
+
+ @Inject
+ protected PermissionsService permissionsService;
+
protected long getUserId() {
UserPrincipal principal = (UserPrincipal) securityContext.getUserPrincipal();
if (principal != null) {
@@ -29,4 +41,5 @@ public class BaseResource {
}
return 0;
}
+
}
diff --git a/src/main/java/org/traccar/api/CorsResponseFilter.java b/src/main/java/org/traccar/api/CorsResponseFilter.java
index 91aea5718..67d0341a1 100644
--- a/src/main/java/org/traccar/api/CorsResponseFilter.java
+++ b/src/main/java/org/traccar/api/CorsResponseFilter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,16 +16,26 @@
package org.traccar.api;
import io.netty.handler.codec.http.HttpHeaderNames;
-import org.traccar.Context;
+import org.traccar.config.Config;
import org.traccar.config.Keys;
+import javax.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 java.io.IOException;
+@Singleton
public class CorsResponseFilter implements ContainerResponseFilter {
+ private final String allowed;
+
+ @Inject
+ public CorsResponseFilter(Config config) {
+ allowed = config.getString(Keys.WEB_ORIGIN);
+ }
+
private static final String ORIGIN_ALL = "*";
private static final String HEADERS_ALL = "origin, content-type, accept, authorization";
private static final String METHODS_ALL = "GET, POST, PUT, DELETE, OPTIONS";
@@ -46,8 +56,6 @@ public class CorsResponseFilter implements ContainerResponseFilter {
if (!response.getHeaders().containsKey(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN.toString())) {
String origin = request.getHeaderString(HttpHeaderNames.ORIGIN.toString());
- String allowed = Context.getConfig().getString(Keys.WEB_ORIGIN);
-
if (origin == null) {
response.getHeaders().add(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN.toString(), ORIGIN_ALL);
} else if (allowed == null || allowed.equals(ORIGIN_ALL) || allowed.contains(origin)) {
diff --git a/src/main/java/org/traccar/api/ExtendedObjectResource.java b/src/main/java/org/traccar/api/ExtendedObjectResource.java
index 9e554217e..8467b46c6 100644
--- a/src/main/java/org/traccar/api/ExtendedObjectResource.java
+++ b/src/main/java/org/traccar/api/ExtendedObjectResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,17 +16,19 @@
*/
package org.traccar.api;
-import java.sql.SQLException;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
+import org.traccar.model.BaseModel;
+import org.traccar.model.Device;
+import org.traccar.model.Group;
+import org.traccar.model.User;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Request;
import javax.ws.rs.GET;
import javax.ws.rs.QueryParam;
-
-import org.traccar.Context;
-import org.traccar.database.ExtendedObjectManager;
-import org.traccar.model.BaseModel;
+import java.util.Collection;
+import java.util.LinkedList;
public class ExtendedObjectResource<T extends BaseModel> extends BaseObjectResource<T> {
@@ -36,27 +38,34 @@ public class ExtendedObjectResource<T extends BaseModel> extends BaseObjectResou
@GET
public Collection<T> get(
- @QueryParam("all") boolean all, @QueryParam("userId") long userId, @QueryParam("groupId") long groupId,
- @QueryParam("deviceId") long deviceId, @QueryParam("refresh") boolean refresh) throws SQLException {
-
- ExtendedObjectManager<T> manager = (ExtendedObjectManager<T>) Context.getManager(getBaseClass());
- if (refresh) {
- manager.refreshItems();
- }
+ @QueryParam("all") boolean all, @QueryParam("userId") long userId,
+ @QueryParam("groupId") long groupId, @QueryParam("deviceId") long deviceId) throws StorageException {
- Set<Long> result = new HashSet<>(getSimpleManagerItems(manager, all, userId));
+ var conditions = new LinkedList<Condition>();
- if (groupId != 0) {
- Context.getPermissionsManager().checkGroup(getUserId(), groupId);
- result.retainAll(manager.getGroupItems(groupId));
+ if (all) {
+ if (permissionsService.notAdmin(getUserId())) {
+ conditions.add(new Condition.Permission(User.class, getUserId(), baseClass));
+ }
+ } else {
+ if (userId == 0) {
+ conditions.add(new Condition.Permission(User.class, getUserId(), baseClass));
+ } else {
+ permissionsService.checkUser(getUserId(), userId);
+ conditions.add(new Condition.Permission(User.class, userId, baseClass).excludeGroups());
+ }
}
- if (deviceId != 0) {
- Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
- result.retainAll(manager.getDeviceItems(deviceId));
+ if (groupId > 0) {
+ permissionsService.checkPermission(Group.class, getUserId(), groupId);
+ conditions.add(new Condition.Permission(Group.class, groupId, baseClass).excludeGroups());
+ }
+ if (deviceId > 0) {
+ permissionsService.checkPermission(Device.class, getUserId(), deviceId);
+ conditions.add(new Condition.Permission(Device.class, deviceId, baseClass).excludeGroups());
}
- return manager.getItems(result);
+ return storage.getObjects(baseClass, new Request(new Columns.All(), Condition.merge(conditions)));
}
}
diff --git a/src/main/java/org/traccar/api/MediaFilter.java b/src/main/java/org/traccar/api/MediaFilter.java
index 77731a810..ab75bdc5d 100644
--- a/src/main/java/org/traccar/api/MediaFilter.java
+++ b/src/main/java/org/traccar/api/MediaFilter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,20 @@
*/
package org.traccar.api;
-import java.io.IOException;
-import java.sql.SQLException;
+import com.google.inject.Provider;
+import org.traccar.api.resource.SessionResource;
+import org.traccar.api.security.PermissionsService;
+import org.traccar.database.StatisticsManager;
+import org.traccar.helper.Log;
+import org.traccar.model.Device;
+import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Request;
+import javax.inject.Inject;
+import javax.inject.Singleton;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
@@ -28,16 +39,24 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
+import java.io.IOException;
-import org.traccar.Context;
-import org.traccar.Main;
-import org.traccar.api.resource.SessionResource;
-import org.traccar.database.StatisticsManager;
-import org.traccar.helper.Log;
-import org.traccar.model.Device;
-
+@Singleton
public class MediaFilter implements Filter {
+ private final Storage storage;
+ private final StatisticsManager statisticsManager;
+ private final Provider<PermissionsService> permissionsServiceProvider;
+
+ @Inject
+ public MediaFilter(
+ Storage storage, StatisticsManager statisticsManager,
+ Provider<PermissionsService> permissionsServiceProvider) {
+ this.storage = storage;
+ this.statisticsManager = statisticsManager;
+ this.permissionsServiceProvider = permissionsServiceProvider;
+ }
+
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@@ -45,6 +64,7 @@ public class MediaFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
+
HttpServletResponse httpResponse = (HttpServletResponse) response;
try {
HttpSession session = ((HttpServletRequest) request).getSession(false);
@@ -52,8 +72,7 @@ public class MediaFilter implements Filter {
if (session != null) {
userId = (Long) session.getAttribute(SessionResource.USER_ID_KEY);
if (userId != null) {
- Context.getPermissionsManager().checkUserEnabled(userId);
- Main.getInjector().getInstance(StatisticsManager.class).registerRequest(userId);
+ statisticsManager.registerRequest(userId);
}
}
if (userId == null) {
@@ -64,21 +83,19 @@ public class MediaFilter implements Filter {
String path = ((HttpServletRequest) request).getPathInfo();
String[] parts = path != null ? path.split("/") : null;
if (parts != null && parts.length >= 2) {
- Device device = Context.getDeviceManager().getByUniqueId(parts[1]);
+ Device device = storage.getObject(Device.class, new Request(
+ new Columns.All(), new Condition.Equals("uniqueId", parts[1])));
if (device != null) {
- Context.getPermissionsManager().checkDevice(userId, device.getId());
+ permissionsServiceProvider.get().checkPermission(Device.class, userId, device.getId());
chain.doFilter(request, response);
return;
}
}
httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
- } catch (SecurityException e) {
+ } catch (SecurityException | StorageException e) {
httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
httpResponse.getWriter().println(Log.exceptionStack(e));
- } catch (SQLException e) {
- httpResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST);
- httpResponse.getWriter().println(Log.exceptionStack(e));
}
}
diff --git a/src/main/java/org/traccar/api/SimpleObjectResource.java b/src/main/java/org/traccar/api/SimpleObjectResource.java
index a7fcae0e7..4a435ca7d 100644
--- a/src/main/java/org/traccar/api/SimpleObjectResource.java
+++ b/src/main/java/org/traccar/api/SimpleObjectResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,15 +16,17 @@
*/
package org.traccar.api;
-import java.sql.SQLException;
-import java.util.Collection;
+import org.traccar.model.BaseModel;
+import org.traccar.model.User;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Request;
import javax.ws.rs.GET;
import javax.ws.rs.QueryParam;
-
-import org.traccar.Context;
-import org.traccar.database.BaseObjectManager;
-import org.traccar.model.BaseModel;
+import java.util.Collection;
+import java.util.LinkedList;
public class SimpleObjectResource<T extends BaseModel> extends BaseObjectResource<T> {
@@ -34,10 +36,24 @@ public class SimpleObjectResource<T extends BaseModel> extends BaseObjectResourc
@GET
public Collection<T> get(
- @QueryParam("all") boolean all, @QueryParam("userId") long userId) throws SQLException {
-
- BaseObjectManager<T> manager = Context.getManager(getBaseClass());
- return manager.getItems(getSimpleManagerItems(manager, all, userId));
+ @QueryParam("all") boolean all, @QueryParam("userId") long userId) throws StorageException {
+
+ var conditions = new LinkedList<Condition>();
+
+ if (all) {
+ if (permissionsService.notAdmin(getUserId())) {
+ conditions.add(new Condition.Permission(User.class, getUserId(), baseClass));
+ }
+ } else {
+ if (userId == 0) {
+ userId = getUserId();
+ } else {
+ permissionsService.checkUser(getUserId(), userId);
+ }
+ conditions.add(new Condition.Permission(User.class, userId, baseClass));
+ }
+
+ return storage.getObjects(baseClass, new Request(new Columns.All(), Condition.merge(conditions)));
}
}
diff --git a/src/main/java/org/traccar/api/resource/AttributeResource.java b/src/main/java/org/traccar/api/resource/AttributeResource.java
index de69d871c..f85e90133 100644
--- a/src/main/java/org/traccar/api/resource/AttributeResource.java
+++ b/src/main/java/org/traccar/api/resource/AttributeResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2017 - 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,7 @@
*/
package org.traccar.api.resource;
-import java.sql.SQLException;
-
+import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.POST;
@@ -29,68 +28,72 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
-import org.traccar.Context;
import org.traccar.api.ExtendedObjectResource;
import org.traccar.model.Attribute;
+import org.traccar.model.Device;
import org.traccar.model.Position;
import org.traccar.handler.ComputedAttributesHandler;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Request;
@Path("attributes/computed")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class AttributeResource extends ExtendedObjectResource<Attribute> {
+ @Inject
+ private ComputedAttributesHandler computedAttributesHandler;
+
public AttributeResource() {
super(Attribute.class);
}
@POST
@Path("test")
- public Response test(@QueryParam("deviceId") long deviceId, Attribute entity) {
- Context.getPermissionsManager().checkAdmin(getUserId());
- Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
- Position last = Context.getIdentityManager().getLastPosition(deviceId);
- if (last != null) {
- Object result = new ComputedAttributesHandler(
- Context.getConfig(),
- Context.getIdentityManager(),
- Context.getAttributesManager()).computeAttribute(entity, last);
- if (result != null) {
- switch (entity.getType()) {
- case "number":
- Number numberValue = (Number) result;
- return Response.ok(numberValue).build();
- case "boolean":
- Boolean booleanValue = (Boolean) result;
- return Response.ok(booleanValue).build();
- default:
- return Response.ok(result.toString()).build();
- }
- } else {
- return Response.noContent().build();
+ public Response test(@QueryParam("deviceId") long deviceId, Attribute entity) throws StorageException {
+ permissionsService.checkAdmin(getUserId());
+ permissionsService.checkPermission(Device.class, getUserId(), deviceId);
+
+ Position position = storage.getObject(Position.class, new Request(
+ new Columns.All(),
+ new Condition.LatestPositions(deviceId)));
+
+ Object result = computedAttributesHandler.computeAttribute(entity, position);
+ if (result != null) {
+ switch (entity.getType()) {
+ case "number":
+ Number numberValue = (Number) result;
+ return Response.ok(numberValue).build();
+ case "boolean":
+ Boolean booleanValue = (Boolean) result;
+ return Response.ok(booleanValue).build();
+ default:
+ return Response.ok(result.toString()).build();
}
} else {
- throw new IllegalArgumentException("Device has no last position");
+ return Response.noContent().build();
}
}
@POST
- public Response add(Attribute entity) throws SQLException {
- Context.getPermissionsManager().checkAdmin(getUserId());
+ public Response add(Attribute entity) throws StorageException {
+ permissionsService.checkAdmin(getUserId());
return super.add(entity);
}
@Path("{id}")
@PUT
- public Response update(Attribute entity) throws SQLException {
- Context.getPermissionsManager().checkAdmin(getUserId());
+ public Response update(Attribute entity) throws StorageException {
+ permissionsService.checkAdmin(getUserId());
return super.update(entity);
}
@Path("{id}")
@DELETE
- public Response remove(@PathParam("id") long id) throws SQLException {
- Context.getPermissionsManager().checkAdmin(getUserId());
+ public Response remove(@PathParam("id") long id) throws StorageException {
+ permissionsService.checkAdmin(getUserId());
return super.remove(id);
}
diff --git a/src/main/java/org/traccar/api/resource/CommandResource.java b/src/main/java/org/traccar/api/resource/CommandResource.java
index a31345246..6ef6ee9c5 100644
--- a/src/main/java/org/traccar/api/resource/CommandResource.java
+++ b/src/main/java/org/traccar/api/resource/CommandResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2016 Gabor Somogyi (gabor.g.somogyi@gmail.com)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
@@ -17,16 +17,24 @@
*/
package org.traccar.api.resource;
-import org.traccar.Context;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.BaseProtocol;
+import org.traccar.ServerManager;
import org.traccar.api.ExtendedObjectResource;
import org.traccar.database.CommandsManager;
import org.traccar.model.Command;
+import org.traccar.model.Device;
+import org.traccar.model.Position;
import org.traccar.model.Typed;
+import org.traccar.model.User;
+import org.traccar.model.UserRestrictions;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Request;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
-
+import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
@@ -35,40 +43,80 @@ import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
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.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
@Path("commands")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class CommandResource extends ExtendedObjectResource<Command> {
+ private static final Logger LOGGER = LoggerFactory.getLogger(CommandResource.class);
+
+ @Inject
+ private CommandsManager commandsManager;
+
+ @Inject
+ private ServerManager serverManager;
+
public CommandResource() {
super(Command.class);
}
+ private BaseProtocol getDeviceProtocol(long deviceId) throws StorageException {
+ Position position = storage.getObject(Position.class, new Request(
+ new Columns.All(), new Condition.LatestPositions(deviceId)));
+ if (position != null) {
+ return serverManager.getProtocol(position.getProtocol());
+ } else {
+ return null;
+ }
+ }
+
@GET
@Path("send")
- public Collection<Command> get(@QueryParam("deviceId") long deviceId) {
- Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
- CommandsManager commandsManager = Context.getCommandsManager();
- Set<Long> result = new HashSet<>(commandsManager.getUserItems(getUserId()));
- result.retainAll(commandsManager.getSupportedCommands(deviceId));
- return commandsManager.getItems(result);
+ public Collection<Command> get(@QueryParam("deviceId") long deviceId) throws StorageException {
+ permissionsService.checkPermission(Device.class, getUserId(), deviceId);
+ BaseProtocol protocol = getDeviceProtocol(deviceId);
+
+ var commands = storage.getObjects(baseClass, new Request(
+ new Columns.All(),
+ Condition.merge(List.of(
+ new Condition.Permission(User.class, getUserId(), baseClass),
+ new Condition.Permission(Device.class, deviceId, baseClass)
+ ))));
+
+ return commands.stream().filter(command -> {
+ String type = command.getType();
+ if (protocol != null) {
+ return command.getTextChannel() && protocol.getSupportedTextCommands().contains(type)
+ || !command.getTextChannel() && protocol.getSupportedDataCommands().contains(type);
+ } else {
+ return type.equals(Command.TYPE_CUSTOM);
+ }
+ }).collect(Collectors.toList());
}
@POST
@Path("send")
public Response send(Command entity) throws Exception {
- Context.getPermissionsManager().checkReadonly(getUserId());
- long deviceId = entity.getDeviceId();
- long id = entity.getId();
- Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
- if (id != 0) {
- Context.getPermissionsManager().checkPermission(Command.class, getUserId(), id);
- Context.getPermissionsManager().checkUserDeviceCommand(getUserId(), deviceId, id);
+ permissionsService.checkRestriction(getUserId(), UserRestrictions::getReadonly);
+ if (entity.getId() > 0) {
+ permissionsService.checkPermission(baseClass, getUserId(), entity.getId());
+ long deviceId = entity.getDeviceId();
+ entity = storage.getObject(baseClass, new Request(
+ new Columns.All(), new Condition.Equals("id", entity.getId())));
+ entity.setDeviceId(deviceId);
} else {
- Context.getPermissionsManager().checkLimitCommands(getUserId());
+ permissionsService.checkRestriction(getUserId(), UserRestrictions::getLimitCommands);
}
- if (!Context.getCommandsManager().sendCommand(entity)) {
+ permissionsService.checkPermission(Device.class, getUserId(), entity.getDeviceId());
+ if (!commandsManager.sendCommand(entity)) {
return Response.accepted(entity).build();
}
return Response.ok(entity).build();
@@ -78,15 +126,33 @@ public class CommandResource extends ExtendedObjectResource<Command> {
@Path("types")
public Collection<Typed> get(
@QueryParam("deviceId") long deviceId,
- @QueryParam("protocol") String protocol,
- @QueryParam("textChannel") boolean textChannel) {
+ @QueryParam("textChannel") boolean textChannel) throws StorageException {
if (deviceId != 0) {
- Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
- return Context.getCommandsManager().getCommandTypes(deviceId, textChannel);
- } else if (protocol != null) {
- return Context.getCommandsManager().getCommandTypes(protocol, textChannel);
+ permissionsService.checkPermission(Device.class, getUserId(), deviceId);
+ BaseProtocol protocol = getDeviceProtocol(deviceId);
+ if (protocol != null) {
+ if (textChannel) {
+ return protocol.getSupportedTextCommands().stream().map(Typed::new).collect(Collectors.toList());
+ } else {
+ return protocol.getSupportedDataCommands().stream().map(Typed::new).collect(Collectors.toList());
+ }
+ } else {
+ return Collections.singletonList(new Typed(Command.TYPE_CUSTOM));
+ }
} else {
- return Context.getCommandsManager().getAllCommandTypes();
+ List<Typed> result = new ArrayList<>();
+ Field[] fields = Command.class.getDeclaredFields();
+ for (Field field : fields) {
+ if (Modifier.isStatic(field.getModifiers()) && field.getName().startsWith("TYPE_")) {
+ try {
+ result.add(new Typed(field.get(null).toString()));
+ } catch (IllegalArgumentException | IllegalAccessException error) {
+ LOGGER.warn("Get command types error", error);
+ }
+ }
+ }
+ return result;
}
}
+
}
diff --git a/src/main/java/org/traccar/api/resource/DeviceResource.java b/src/main/java/org/traccar/api/resource/DeviceResource.java
index 7006cdb84..c0b0cea0d 100644
--- a/src/main/java/org/traccar/api/resource/DeviceResource.java
+++ b/src/main/java/org/traccar/api/resource/DeviceResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,33 +15,58 @@
*/
package org.traccar.api.resource;
-import org.traccar.Context;
import org.traccar.api.BaseObjectResource;
-import org.traccar.database.DeviceManager;
+import org.traccar.broadcast.BroadcastService;
+import org.traccar.database.MediaManager;
import org.traccar.helper.LogAction;
import org.traccar.model.Device;
import org.traccar.model.DeviceAccumulators;
+import org.traccar.model.Position;
+import org.traccar.model.User;
+import org.traccar.session.ConnectionManager;
+import org.traccar.session.cache.CacheManager;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Request;
+import javax.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 java.sql.SQLException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
import java.util.Collection;
-import java.util.HashSet;
+import java.util.LinkedList;
import java.util.List;
-import java.util.Set;
@Path("devices")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class DeviceResource extends BaseObjectResource<Device> {
+ @Inject
+ private CacheManager cacheManager;
+
+ @Inject
+ private ConnectionManager connectionManager;
+
+ @Inject
+ private BroadcastService broadcastService;
+
+ @Inject
+ private MediaManager mediaManager;
+
public DeviceResource() {
super(Device.class);
}
@@ -50,51 +75,112 @@ public class DeviceResource extends BaseObjectResource<Device> {
public Collection<Device> get(
@QueryParam("all") boolean all, @QueryParam("userId") long userId,
@QueryParam("uniqueId") List<String> uniqueIds,
- @QueryParam("id") List<Long> deviceIds) throws SQLException {
- DeviceManager deviceManager = Context.getDeviceManager();
- Set<Long> result;
- if (all) {
- if (Context.getPermissionsManager().getUserAdmin(getUserId())) {
- result = deviceManager.getAllItems();
- } else {
- Context.getPermissionsManager().checkManager(getUserId());
- result = deviceManager.getManagedItems(getUserId());
- }
- } else if (uniqueIds.isEmpty() && deviceIds.isEmpty()) {
- if (userId == 0) {
- userId = getUserId();
- }
- Context.getPermissionsManager().checkUser(getUserId(), userId);
- if (Context.getPermissionsManager().getUserAdmin(getUserId())) {
- result = deviceManager.getAllUserItems(userId);
- } else {
- result = deviceManager.getUserItems(userId);
- }
- } else {
- result = new HashSet<>();
+ @QueryParam("id") List<Long> deviceIds) throws StorageException {
+
+ if (!uniqueIds.isEmpty() || !deviceIds.isEmpty()) {
+
+ List<Device> result = new LinkedList<>();
for (String uniqueId : uniqueIds) {
- Device device = deviceManager.getByUniqueId(uniqueId);
- Context.getPermissionsManager().checkDevice(getUserId(), device.getId());
- result.add(device.getId());
+ result.addAll(storage.getObjects(Device.class, new Request(
+ new Columns.All(),
+ new Condition.And(
+ new Condition.Equals("uniqueId", uniqueId),
+ new Condition.Permission(User.class, getUserId(), Device.class)))));
}
for (Long deviceId : deviceIds) {
- Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
- result.add(deviceId);
+ result.addAll(storage.getObjects(Device.class, new Request(
+ new Columns.All(),
+ new Condition.And(
+ new Condition.Equals("id", deviceId),
+ new Condition.Permission(User.class, getUserId(), Device.class)))));
}
+ return result;
+
+ } else {
+
+ var conditions = new LinkedList<Condition>();
+
+ if (all) {
+ if (permissionsService.notAdmin(getUserId())) {
+ conditions.add(new Condition.Permission(User.class, getUserId(), baseClass));
+ }
+ } else {
+ if (userId == 0) {
+ conditions.add(new Condition.Permission(User.class, getUserId(), baseClass));
+ } else {
+ permissionsService.checkUser(getUserId(), userId);
+ conditions.add(new Condition.Permission(User.class, userId, baseClass).excludeGroups());
+ }
+ }
+
+ return storage.getObjects(baseClass, new Request(new Columns.All(), Condition.merge(conditions)));
+
}
- return deviceManager.getItems(result);
}
@Path("{id}/accumulators")
@PUT
- public Response updateAccumulators(DeviceAccumulators entity) throws SQLException {
- if (!Context.getPermissionsManager().getUserAdmin(getUserId())) {
- Context.getPermissionsManager().checkManager(getUserId());
- Context.getPermissionsManager().checkPermission(Device.class, getUserId(), entity.getDeviceId());
+ public Response updateAccumulators(DeviceAccumulators entity) throws StorageException {
+ if (permissionsService.notAdmin(getUserId())) {
+ permissionsService.checkManager(getUserId());
+ permissionsService.checkPermission(Device.class, getUserId(), entity.getDeviceId());
}
- Context.getDeviceManager().resetDeviceAccumulators(entity);
+
+ Position position = storage.getObject(Position.class, new Request(
+ new Columns.All(), new Condition.LatestPositions(entity.getDeviceId())));
+ if (position != null) {
+ if (entity.getTotalDistance() != null) {
+ position.getAttributes().put(Position.KEY_TOTAL_DISTANCE, entity.getTotalDistance());
+ }
+ if (entity.getHours() != null) {
+ position.getAttributes().put(Position.KEY_HOURS, entity.getHours());
+ }
+ position.setId(storage.addObject(position, new Request(new Columns.Exclude("id"))));
+
+ Device device = new Device();
+ device.setId(position.getDeviceId());
+ device.setPositionId(position.getId());
+ storage.updateObject(device, new Request(
+ new Columns.Include("positionId"),
+ new Condition.Equals("id", device.getId())));
+
+ try {
+ cacheManager.addDevice(position.getDeviceId());
+ cacheManager.updatePosition(position);
+ connectionManager.updatePosition(true, position);
+ } finally {
+ cacheManager.removeDevice(position.getDeviceId());
+ }
+ } else {
+ throw new IllegalArgumentException();
+ }
+
LogAction.resetDeviceAccumulators(getUserId(), entity.getDeviceId());
return Response.noContent().build();
}
+ @Path("{id}/image")
+ @POST
+ @Consumes("image/*")
+ public Response uploadImage(
+ @PathParam("id") long deviceId, File file,
+ @HeaderParam(HttpHeaders.CONTENT_TYPE) String type) throws StorageException, IOException {
+
+ Device device = storage.getObject(Device.class, new Request(
+ new Columns.All(),
+ new Condition.And(
+ new Condition.Equals("id", deviceId),
+ new Condition.Permission(User.class, getUserId(), Device.class))));
+ if (device != null) {
+ String name = "device";
+ String extension = type.substring("image/".length());
+ try (var input = new FileInputStream(file);
+ var output = mediaManager.createFileStream(device.getUniqueId(), name, extension)) {
+ input.transferTo(output);
+ }
+ return Response.ok(name + "." + extension).build();
+ }
+ return Response.status(Response.Status.NOT_FOUND).build();
+ }
+
}
diff --git a/src/main/java/org/traccar/api/resource/EventResource.java b/src/main/java/org/traccar/api/resource/EventResource.java
index 34e4a94ce..afdaf52b5 100644
--- a/src/main/java/org/traccar/api/resource/EventResource.java
+++ b/src/main/java/org/traccar/api/resource/EventResource.java
@@ -15,7 +15,13 @@
*/
package org.traccar.api.resource;
-import java.sql.SQLException;
+import org.traccar.api.BaseResource;
+import org.traccar.model.Device;
+import org.traccar.model.Event;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Request;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
@@ -26,32 +32,20 @@ import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
-import org.traccar.Context;
-import org.traccar.api.BaseResource;
-import org.traccar.model.Event;
-import org.traccar.model.Geofence;
-import org.traccar.model.Maintenance;
-
@Path("events")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
-
public class EventResource extends BaseResource {
@Path("{id}")
@GET
- public Event get(@PathParam("id") long id) throws SQLException {
- Event event = Context.getDataManager().getObject(Event.class, id);
+ public Event get(@PathParam("id") long id) throws StorageException {
+ Event event = storage.getObject(Event.class, new Request(
+ new Columns.All(), new Condition.Equals("id", id)));
if (event == null) {
throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build());
}
- Context.getPermissionsManager().checkDevice(getUserId(), event.getDeviceId());
- if (event.getGeofenceId() != 0) {
- Context.getPermissionsManager().checkPermission(Geofence.class, getUserId(), event.getGeofenceId());
- }
- if (event.getMaintenanceId() != 0) {
- Context.getPermissionsManager().checkPermission(Maintenance.class, getUserId(), event.getMaintenanceId());
- }
+ permissionsService.checkPermission(Device.class, getUserId(), event.getDeviceId());
return event;
}
diff --git a/src/main/java/org/traccar/api/resource/NotificationResource.java b/src/main/java/org/traccar/api/resource/NotificationResource.java
index 9631a52b7..2e4ad12f3 100644
--- a/src/main/java/org/traccar/api/resource/NotificationResource.java
+++ b/src/main/java/org/traccar/api/resource/NotificationResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,8 +15,18 @@
*/
package org.traccar.api.resource;
-import java.util.Collection;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.api.ExtendedObjectResource;
+import org.traccar.model.Event;
+import org.traccar.model.Notification;
+import org.traccar.model.Typed;
+import org.traccar.model.User;
+import org.traccar.notification.MessageException;
+import org.traccar.notification.NotificatorManager;
+import org.traccar.storage.StorageException;
+import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
@@ -25,20 +35,22 @@ import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
-
-import org.traccar.Context;
-import org.traccar.api.ExtendedObjectResource;
-import org.traccar.model.Event;
-import org.traccar.model.Notification;
-import org.traccar.model.Typed;
-import org.traccar.notification.MessageException;
-
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
@Path("notifications")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class NotificationResource extends ExtendedObjectResource<Notification> {
+ private static final Logger LOGGER = LoggerFactory.getLogger(NotificationResource.class);
+
+ @Inject
+ private NotificatorManager notificatorManager;
+
public NotificationResource() {
super(Notification.class);
}
@@ -46,21 +58,32 @@ public class NotificationResource extends ExtendedObjectResource<Notification> {
@GET
@Path("types")
public Collection<Typed> get() {
- return Context.getNotificationManager().getAllNotificationTypes();
+ List<Typed> types = new LinkedList<>();
+ Field[] fields = Event.class.getDeclaredFields();
+ for (Field field : fields) {
+ if (Modifier.isStatic(field.getModifiers()) && field.getName().startsWith("TYPE_")) {
+ try {
+ types.add(new Typed(field.get(null).toString()));
+ } catch (IllegalArgumentException | IllegalAccessException error) {
+ LOGGER.warn("Get event types error", error);
+ }
+ }
+ }
+ return types;
}
@GET
@Path("notificators")
public Collection<Typed> getNotificators() {
- return Context.getNotificatorManager().getAllNotificatorTypes();
+ return notificatorManager.getAllNotificatorTypes();
}
@POST
@Path("test")
- public Response testMessage() throws MessageException, InterruptedException {
- for (Typed method : Context.getNotificatorManager().getAllNotificatorTypes()) {
- Context.getNotificatorManager()
- .getNotificator(method.getType()).sendSync(getUserId(), new Event("test", 0), null);
+ public Response testMessage() throws MessageException, InterruptedException, StorageException {
+ User user = permissionsService.getUser(getUserId());
+ for (Typed method : notificatorManager.getAllNotificatorTypes()) {
+ notificatorManager.getNotificator(method.getType()).send(user, new Event("test", 0), null);
}
return Response.noContent().build();
}
@@ -68,8 +91,9 @@ public class NotificationResource extends ExtendedObjectResource<Notification> {
@POST
@Path("test/{notificator}")
public Response testMessage(@PathParam("notificator") String notificator)
- throws MessageException, InterruptedException {
- Context.getNotificatorManager().getNotificator(notificator).sendSync(getUserId(), new Event("test", 0), null);
+ throws MessageException, InterruptedException, StorageException {
+ User user = permissionsService.getUser(getUserId());
+ notificatorManager.getNotificator(notificator).send(user, new Event("test", 0), null);
return Response.noContent().build();
}
diff --git a/src/main/java/org/traccar/api/resource/PasswordResource.java b/src/main/java/org/traccar/api/resource/PasswordResource.java
index 1868a6191..2d87a8665 100644
--- a/src/main/java/org/traccar/api/resource/PasswordResource.java
+++ b/src/main/java/org/traccar/api/resource/PasswordResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,14 +15,18 @@
*/
package org.traccar.api.resource;
-import org.apache.velocity.VelocityContext;
-import org.traccar.Context;
import org.traccar.api.BaseResource;
+import org.traccar.api.signature.TokenManager;
+import org.traccar.mail.MailManager;
import org.traccar.model.User;
-import org.traccar.notification.NotificationMessage;
import org.traccar.notification.TextTemplateFormatter;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Request;
import javax.annotation.security.PermitAll;
+import javax.inject.Inject;
import javax.mail.MessagingException;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
@@ -31,33 +35,35 @@ import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
-import java.sql.SQLException;
-import java.util.UUID;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
@Path("password")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public class PasswordResource extends BaseResource {
- private static final String PASSWORD_RESET_TOKEN = "passwordToken";
+ @Inject
+ private MailManager mailManager;
+
+ @Inject
+ private TokenManager tokenManager;
+
+ @Inject
+ private TextTemplateFormatter textTemplateFormatter;
@Path("reset")
@PermitAll
@POST
- public Response reset(@FormParam("email") String email) throws SQLException, MessagingException {
- for (long userId : Context.getUsersManager().getAllItems()) {
- User user = Context.getUsersManager().getById(userId);
- if (email.equals(user.getEmail())) {
- String token = UUID.randomUUID().toString().replaceAll("-", "");
- user.set(PASSWORD_RESET_TOKEN, token);
- Context.getUsersManager().updateItem(user);
- VelocityContext velocityContext = TextTemplateFormatter.prepareContext(null);
- velocityContext.put("token", token);
- NotificationMessage fullMessage =
- TextTemplateFormatter.formatMessage(velocityContext, "passwordReset", "full");
- Context.getMailManager().sendMessage(userId, fullMessage.getSubject(), fullMessage.getBody());
- break;
- }
+ public Response reset(@FormParam("email") String email)
+ throws StorageException, MessagingException, GeneralSecurityException, IOException {
+
+ User user = storage.getObject(User.class, new Request(
+ new Columns.All(), new Condition.Equals("email", email)));
+ if (user != null) {
+ var velocityContext = textTemplateFormatter.prepareContext(permissionsService.getServer(), user);
+ var fullMessage = textTemplateFormatter.formatMessage(velocityContext, "passwordReset", "full");
+ mailManager.sendMessage(user, fullMessage.getSubject(), fullMessage.getBody());
}
return Response.ok().build();
}
@@ -66,15 +72,18 @@ public class PasswordResource extends BaseResource {
@PermitAll
@POST
public Response update(
- @FormParam("token") String token, @FormParam("password") String password) throws SQLException {
- for (long userId : Context.getUsersManager().getAllItems()) {
- User user = Context.getUsersManager().getById(userId);
- if (token.equals(user.getString(PASSWORD_RESET_TOKEN))) {
- user.getAttributes().remove(PASSWORD_RESET_TOKEN);
- user.setPassword(password);
- Context.getUsersManager().updateItem(user);
- return Response.ok().build();
- }
+ @FormParam("token") String token, @FormParam("password") String password)
+ throws StorageException, GeneralSecurityException, IOException {
+
+ long userId = tokenManager.verifyToken(token);
+ User user = storage.getObject(User.class, new Request(
+ new Columns.All(), new Condition.Equals("id", userId)));
+ if (user != null) {
+ user.setPassword(password);
+ storage.updateObject(user, new Request(
+ new Columns.Include("hashedPassword", "salt"),
+ new Condition.Equals("id", userId)));
+ return Response.ok().build();
}
return Response.status(Response.Status.NOT_FOUND).build();
}
diff --git a/src/main/java/org/traccar/api/resource/PermissionsResource.java b/src/main/java/org/traccar/api/resource/PermissionsResource.java
index 54d3964b6..d35cb98bb 100644
--- a/src/main/java/org/traccar/api/resource/PermissionsResource.java
+++ b/src/main/java/org/traccar/api/resource/PermissionsResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,12 +16,14 @@
*/
package org.traccar.api.resource;
-import java.sql.SQLException;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Set;
+import org.traccar.api.BaseResource;
+import org.traccar.helper.LogAction;
+import org.traccar.model.Permission;
+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;
@@ -30,33 +32,24 @@ import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
-
-import org.traccar.Context;
-import org.traccar.api.BaseResource;
-import org.traccar.helper.LogAction;
-import org.traccar.model.Device;
-import org.traccar.model.Permission;
-import org.traccar.model.User;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Set;
@Path("permissions")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class PermissionsResource extends BaseResource {
- private void checkPermission(Permission permission, boolean link) {
- if (!link && permission.getOwnerClass().equals(User.class)
- && permission.getPropertyClass().equals(Device.class)) {
- if (getUserId() != permission.getOwnerId()) {
- Context.getPermissionsManager().checkUser(getUserId(), permission.getOwnerId());
- } else {
- Context.getPermissionsManager().checkAdmin(getUserId());
- }
- } else {
- Context.getPermissionsManager().checkPermission(
- permission.getOwnerClass(), getUserId(), permission.getOwnerId());
+ @Inject
+ private CacheManager cacheManager;
+
+ private void checkPermission(Permission permission) throws StorageException {
+ if (permissionsService.notAdmin(getUserId())) {
+ permissionsService.checkPermission(permission.getOwnerClass(), getUserId(), permission.getOwnerId());
+ permissionsService.checkPermission(permission.getOwnerClass(), getUserId(), permission.getOwnerId());
}
- Context.getPermissionsManager().checkPermission(
- permission.getPropertyClass(), getUserId(), permission.getPropertyId());
}
private void checkPermissionTypes(List<LinkedHashMap<String, Long>> entities) {
@@ -71,49 +64,51 @@ public class PermissionsResource extends BaseResource {
@Path("bulk")
@POST
- public Response add(List<LinkedHashMap<String, Long>> entities) throws SQLException, ClassNotFoundException {
- Context.getPermissionsManager().checkReadonly(getUserId());
+ public Response add(List<LinkedHashMap<String, Long>> entities) throws StorageException, ClassNotFoundException {
+ permissionsService.checkRestriction(getUserId(), UserRestrictions::getReadonly);
checkPermissionTypes(entities);
for (LinkedHashMap<String, Long> entity: entities) {
Permission permission = new Permission(entity);
- checkPermission(permission, true);
- Context.getDataManager().linkObject(permission.getOwnerClass(), permission.getOwnerId(),
- permission.getPropertyClass(), permission.getPropertyId(), true);
- LogAction.link(getUserId(), permission.getOwnerClass(), permission.getOwnerId(),
+ checkPermission(permission);
+ storage.addPermission(permission);
+ cacheManager.invalidatePermission(
+ true,
+ permission.getOwnerClass(), permission.getOwnerId(),
+ permission.getPropertyClass(), permission.getPropertyId());
+ LogAction.link(getUserId(),
+ permission.getOwnerClass(), permission.getOwnerId(),
permission.getPropertyClass(), permission.getPropertyId());
- }
- if (!entities.isEmpty()) {
- Context.getPermissionsManager().refreshPermissions(new Permission(entities.get(0)));
}
return Response.noContent().build();
}
@POST
- public Response add(LinkedHashMap<String, Long> entity) throws SQLException, ClassNotFoundException {
+ public Response add(LinkedHashMap<String, Long> entity) throws StorageException, ClassNotFoundException {
return add(Collections.singletonList(entity));
}
@DELETE
@Path("bulk")
- public Response remove(List<LinkedHashMap<String, Long>> entities) throws SQLException, ClassNotFoundException {
- Context.getPermissionsManager().checkReadonly(getUserId());
+ public Response remove(List<LinkedHashMap<String, Long>> entities) throws StorageException, ClassNotFoundException {
+ permissionsService.checkRestriction(getUserId(), UserRestrictions::getReadonly);
checkPermissionTypes(entities);
for (LinkedHashMap<String, Long> entity: entities) {
Permission permission = new Permission(entity);
- checkPermission(permission, false);
- Context.getDataManager().linkObject(permission.getOwnerClass(), permission.getOwnerId(),
- permission.getPropertyClass(), permission.getPropertyId(), false);
- LogAction.unlink(getUserId(), permission.getOwnerClass(), permission.getOwnerId(),
+ checkPermission(permission);
+ storage.removePermission(permission);
+ cacheManager.invalidatePermission(
+ true,
+ permission.getOwnerClass(), permission.getOwnerId(),
+ permission.getPropertyClass(), permission.getPropertyId());
+ LogAction.unlink(getUserId(),
+ permission.getOwnerClass(), permission.getOwnerId(),
permission.getPropertyClass(), permission.getPropertyId());
- }
- if (!entities.isEmpty()) {
- Context.getPermissionsManager().refreshPermissions(new Permission(entities.get(0)));
}
return Response.noContent().build();
}
@DELETE
- public Response remove(LinkedHashMap<String, Long> entity) throws SQLException, ClassNotFoundException {
+ public Response remove(LinkedHashMap<String, Long> entity) throws StorageException, ClassNotFoundException {
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 998d59706..042dd1e23 100644
--- a/src/main/java/org/traccar/api/resource/PositionResource.java
+++ b/src/main/java/org/traccar/api/resource/PositionResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,20 +15,32 @@
*/
package org.traccar.api.resource;
-import org.traccar.Context;
import org.traccar.api.BaseResource;
+import org.traccar.helper.model.PositionUtil;
+import org.traccar.model.Device;
import org.traccar.model.Position;
+import org.traccar.model.UserRestrictions;
+import org.traccar.reports.CsvExportProvider;
+import org.traccar.reports.GpxExportProvider;
+import org.traccar.reports.KmlExportProvider;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Request;
+import javax.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 java.sql.SQLException;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.StreamingOutput;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.Date;
import java.util.List;
@@ -37,29 +49,95 @@ import java.util.List;
@Consumes(MediaType.APPLICATION_JSON)
public class PositionResource extends BaseResource {
+ @Inject
+ private KmlExportProvider kmlExportProvider;
+
+ @Inject
+ private CsvExportProvider csvExportProvider;
+
+ @Inject
+ private GpxExportProvider gpxExportProvider;
+
@GET
public Collection<Position> getJson(
@QueryParam("deviceId") long deviceId, @QueryParam("id") List<Long> positionIds,
@QueryParam("from") Date from, @QueryParam("to") Date to)
- throws SQLException {
+ throws StorageException {
if (!positionIds.isEmpty()) {
- ArrayList<Position> positions = new ArrayList<>();
- for (Long positionId : positionIds) {
- Position position = Context.getDataManager().getObject(Position.class, positionId);
- Context.getPermissionsManager().checkDevice(getUserId(), position.getDeviceId());
+ var positions = new ArrayList<Position>();
+ for (long positionId : positionIds) {
+ Position position = storage.getObject(Position.class, new Request(
+ new Columns.All(), new Condition.Equals("id", positionId)));
+ permissionsService.checkPermission(Device.class, getUserId(), position.getDeviceId());
positions.add(position);
}
return positions;
- } else if (deviceId == 0) {
- return Context.getDeviceManager().getInitialState(getUserId());
- } else {
- Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
+ } else if (deviceId > 0) {
+ permissionsService.checkPermission(Device.class, getUserId(), deviceId);
if (from != null && to != null) {
- return Context.getDataManager().getPositions(deviceId, from, to);
+ permissionsService.checkRestriction(getUserId(), UserRestrictions::getDisableReports);
+ return PositionUtil.getPositions(storage, deviceId, from, to);
} else {
- return Collections.singleton(Context.getDeviceManager().getLastPosition(deviceId));
+ return storage.getObjects(Position.class, new Request(
+ new Columns.All(), new Condition.LatestPositions(deviceId)));
}
+ } else {
+ return PositionUtil.getLatestPositions(storage, getUserId());
}
}
+ @Path("kml")
+ @GET
+ @Produces("application/vnd.google-earth.kml+xml")
+ public Response getKml(
+ @QueryParam("deviceId") long deviceId,
+ @QueryParam("from") Date from, @QueryParam("to") Date to) throws StorageException {
+ permissionsService.checkPermission(Device.class, getUserId(), deviceId);
+ StreamingOutput stream = output -> {
+ try {
+ kmlExportProvider.generate(output, deviceId, from, to);
+ } catch (StorageException e) {
+ throw new WebApplicationException(e);
+ }
+ };
+ return Response.ok(stream)
+ .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=positions.kml").build();
+ }
+
+ @Path("csv")
+ @GET
+ @Produces("text/csv")
+ public Response getCsv(
+ @QueryParam("deviceId") long deviceId,
+ @QueryParam("from") Date from, @QueryParam("to") Date to) throws StorageException {
+ permissionsService.checkPermission(Device.class, getUserId(), deviceId);
+ StreamingOutput stream = output -> {
+ try {
+ csvExportProvider.generate(output, deviceId, from, to);
+ } catch (StorageException e) {
+ throw new WebApplicationException(e);
+ }
+ };
+ return Response.ok(stream)
+ .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=positions.csv").build();
+ }
+
+ @Path("gpx")
+ @GET
+ @Produces("application/gpx+xml")
+ public Response getGpx(
+ @QueryParam("deviceId") long deviceId,
+ @QueryParam("from") Date from, @QueryParam("to") Date to) throws StorageException {
+ permissionsService.checkPermission(Device.class, getUserId(), deviceId);
+ StreamingOutput stream = output -> {
+ try {
+ gpxExportProvider.generate(output, deviceId, from, to);
+ } catch (StorageException e) {
+ throw new WebApplicationException(e);
+ }
+ };
+ return Response.ok(stream)
+ .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=positions.gpx").build();
+ }
+
}
diff --git a/src/main/java/org/traccar/api/resource/ReportResource.java b/src/main/java/org/traccar/api/resource/ReportResource.java
index 7347bfd64..70177dd4d 100644
--- a/src/main/java/org/traccar/api/resource/ReportResource.java
+++ b/src/main/java/org/traccar/api/resource/ReportResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2016 - 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,41 +16,47 @@
*/
package org.traccar.api.resource;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.sql.SQLException;
-import java.util.Collection;
-import java.util.Date;
-import java.util.List;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.api.BaseResource;
+import org.traccar.mail.MailManager;
+import org.traccar.helper.LogAction;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+import org.traccar.model.User;
+import org.traccar.model.UserRestrictions;
+import org.traccar.reports.EventsReportProvider;
+import org.traccar.reports.RouteReportProvider;
+import org.traccar.reports.StopsReportProvider;
+import org.traccar.reports.SummaryReportProvider;
+import org.traccar.reports.TripsReportProvider;
+import org.traccar.reports.model.StopReportItem;
+import org.traccar.reports.model.SummaryReportItem;
+import org.traccar.reports.model.TripReportItem;
+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 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 org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.traccar.Context;
-import org.traccar.api.BaseResource;
-import org.traccar.helper.LogAction;
-import org.traccar.model.Event;
-import org.traccar.model.Position;
-import org.traccar.reports.Events;
-import org.traccar.reports.Summary;
-import org.traccar.reports.Trips;
-import org.traccar.reports.model.StopReport;
-import org.traccar.reports.model.SummaryReport;
-import org.traccar.reports.model.TripReport;
-import org.traccar.reports.Route;
-import org.traccar.reports.Stops;
+import javax.ws.rs.core.StreamingOutput;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
@Path("reports")
@Produces(MediaType.APPLICATION_JSON)
@@ -59,155 +65,267 @@ public class ReportResource extends BaseResource {
private static final Logger LOGGER = LoggerFactory.getLogger(ReportResource.class);
- private static final String XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
- private static final String CONTENT_DISPOSITION_VALUE_XLSX = "attachment; filename=report.xlsx";
+ private static final String EXCEL = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
+
+ @Inject
+ private EventsReportProvider eventsReportProvider;
+
+ @Inject
+ private RouteReportProvider routeReportProvider;
+
+ @Inject
+ private StopsReportProvider stopsReportProvider;
+
+ @Inject
+ private SummaryReportProvider summaryReportProvider;
+
+ @Inject
+ private TripsReportProvider tripsReportProvider;
+
+ @Inject
+ private MailManager mailManager;
private interface ReportExecutor {
- void execute(ByteArrayOutputStream stream) throws SQLException, IOException;
+ void execute(OutputStream stream) throws StorageException, IOException;
}
private Response executeReport(
- long userId, boolean mail, ReportExecutor executor) throws SQLException, IOException {
- final ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ long userId, boolean mail, ReportExecutor executor) {
if (mail) {
new Thread(() -> {
try {
+ var stream = new ByteArrayOutputStream();
executor.execute(stream);
MimeBodyPart attachment = new MimeBodyPart();
-
attachment.setFileName("report.xlsx");
attachment.setDataHandler(new DataHandler(new ByteArrayDataSource(
stream.toByteArray(), "application/octet-stream")));
- Context.getMailManager().sendMessage(
- userId, "Report", "The report is in the attachment.", attachment);
- } catch (SQLException | IOException | MessagingException e) {
+ User user = permissionsService.getUser(userId);
+ mailManager.sendMessage(user, "Report", "The report is in the attachment.", attachment);
+ } catch (StorageException | IOException | MessagingException e) {
LOGGER.warn("Report failed", e);
}
}).start();
return Response.noContent().build();
} else {
- executor.execute(stream);
- return Response.ok(stream.toByteArray())
- .header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE_XLSX).build();
+ StreamingOutput stream = output -> {
+ try {
+ executor.execute(output);
+ } catch (StorageException e) {
+ throw new WebApplicationException(e);
+ }
+ };
+ return Response.ok(stream)
+ .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=report.xlsx").build();
}
}
@Path("route")
@GET
public Collection<Position> getRoute(
- @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
- @QueryParam("from") Date from, @QueryParam("to") Date to) throws SQLException {
+ @QueryParam("deviceId") List<Long> deviceIds,
+ @QueryParam("groupId") List<Long> groupIds,
+ @QueryParam("from") Date from,
+ @QueryParam("to") Date to) throws StorageException {
+ permissionsService.checkRestriction(getUserId(), UserRestrictions::getDisableReports);
LogAction.logReport(getUserId(), "route", from, to, deviceIds, groupIds);
- return Route.getObjects(getUserId(), deviceIds, groupIds, from, to);
+ return routeReportProvider.getObjects(getUserId(), deviceIds, groupIds, from, to);
}
@Path("route")
@GET
- @Produces(XLSX)
+ @Produces(EXCEL)
public Response getRouteExcel(
- @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
- @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("mail") boolean mail)
- throws SQLException, IOException {
+ @QueryParam("deviceId") List<Long> deviceIds,
+ @QueryParam("groupId") List<Long> groupIds,
+ @QueryParam("from") Date from,
+ @QueryParam("to") Date to,
+ @QueryParam("mail") boolean mail) throws StorageException {
+ permissionsService.checkRestriction(getUserId(), UserRestrictions::getDisableReports);
return executeReport(getUserId(), mail, stream -> {
LogAction.logReport(getUserId(), "route", from, to, deviceIds, groupIds);
- Route.getExcel(stream, getUserId(), deviceIds, groupIds, from, to);
+ routeReportProvider.getExcel(stream, getUserId(), deviceIds, groupIds, from, to);
});
}
+ @Path("route/{type:xlsx|mail}")
+ @GET
+ @Produces(EXCEL)
+ public Response getRouteExcel(
+ @QueryParam("deviceId") List<Long> deviceIds,
+ @QueryParam("groupId") final List<Long> groupIds,
+ @QueryParam("from") Date from,
+ @QueryParam("to") Date to,
+ @PathParam("type") String type) throws StorageException {
+ return getRouteExcel(deviceIds, groupIds, from, to, type.equals("mail"));
+ }
+
@Path("events")
@GET
public Collection<Event> getEvents(
- @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
- @QueryParam("type") final List<String> types,
- @QueryParam("from") Date from, @QueryParam("to") Date to) throws SQLException {
+ @QueryParam("deviceId") List<Long> deviceIds,
+ @QueryParam("groupId") List<Long> groupIds,
+ @QueryParam("type") List<String> types,
+ @QueryParam("from") Date from,
+ @QueryParam("to") Date to) throws StorageException {
+ permissionsService.checkRestriction(getUserId(), UserRestrictions::getDisableReports);
LogAction.logReport(getUserId(), "events", from, to, deviceIds, groupIds);
- return Events.getObjects(getUserId(), deviceIds, groupIds, types, from, to);
+ return eventsReportProvider.getObjects(getUserId(), deviceIds, groupIds, types, from, to);
}
@Path("events")
@GET
- @Produces(XLSX)
+ @Produces(EXCEL)
public Response getEventsExcel(
- @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
- @QueryParam("type") final List<String> types,
- @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("mail") boolean mail)
- throws SQLException, IOException {
+ @QueryParam("deviceId") List<Long> deviceIds,
+ @QueryParam("groupId") List<Long> groupIds,
+ @QueryParam("type") List<String> types,
+ @QueryParam("from") Date from,
+ @QueryParam("to") Date to,
+ @QueryParam("mail") boolean mail) throws StorageException {
+ permissionsService.checkRestriction(getUserId(), UserRestrictions::getDisableReports);
return executeReport(getUserId(), mail, stream -> {
LogAction.logReport(getUserId(), "events", from, to, deviceIds, groupIds);
- Events.getExcel(stream, getUserId(), deviceIds, groupIds, types, from, to);
+ eventsReportProvider.getExcel(stream, getUserId(), deviceIds, groupIds, types, from, to);
});
}
+ @Path("events/{type:xlsx|mail}")
+ @GET
+ @Produces(EXCEL)
+ public Response getEventsExcel(
+ @QueryParam("deviceId") List<Long> deviceIds,
+ @QueryParam("groupId") List<Long> groupIds,
+ @QueryParam("type") List<String> types,
+ @QueryParam("from") Date from,
+ @QueryParam("to") Date to,
+ @PathParam("type") String type) throws StorageException {
+ return getEventsExcel(deviceIds, groupIds, types, from, to, type.equals("mail"));
+ }
+
@Path("summary")
@GET
- public Collection<SummaryReport> getSummary(
- @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
- @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("daily") boolean daily)
- throws SQLException {
+ public Collection<SummaryReportItem> getSummary(
+ @QueryParam("deviceId") List<Long> deviceIds,
+ @QueryParam("groupId") List<Long> groupIds,
+ @QueryParam("from") Date from,
+ @QueryParam("to") Date to,
+ @QueryParam("daily") boolean daily) throws StorageException {
+ permissionsService.checkRestriction(getUserId(), UserRestrictions::getDisableReports);
LogAction.logReport(getUserId(), "summary", from, to, deviceIds, groupIds);
- return Summary.getObjects(getUserId(), deviceIds, groupIds, from, to, daily);
+ return summaryReportProvider.getObjects(getUserId(), deviceIds, groupIds, from, to, daily);
}
@Path("summary")
@GET
- @Produces(XLSX)
+ @Produces(EXCEL)
public Response getSummaryExcel(
- @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
- @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("daily") boolean daily,
- @QueryParam("mail") boolean mail)
- throws SQLException, IOException {
+ @QueryParam("deviceId") List<Long> deviceIds,
+ @QueryParam("groupId") List<Long> groupIds,
+ @QueryParam("from") Date from,
+ @QueryParam("to") Date to,
+ @QueryParam("daily") boolean daily,
+ @QueryParam("mail") boolean mail) throws StorageException {
+ permissionsService.checkRestriction(getUserId(), UserRestrictions::getDisableReports);
return executeReport(getUserId(), mail, stream -> {
LogAction.logReport(getUserId(), "summary", from, to, deviceIds, groupIds);
- Summary.getExcel(stream, getUserId(), deviceIds, groupIds, from, to, daily);
+ summaryReportProvider.getExcel(stream, getUserId(), deviceIds, groupIds, from, to, daily);
});
}
+ @Path("summary/{type:xlsx|mail}")
+ @GET
+ @Produces(EXCEL)
+ public Response getSummaryExcel(
+ @QueryParam("deviceId") List<Long> deviceIds,
+ @QueryParam("groupId") List<Long> groupIds,
+ @QueryParam("from") Date from,
+ @QueryParam("to") Date to,
+ @QueryParam("daily") boolean daily,
+ @PathParam("type") String type) throws StorageException {
+ return getSummaryExcel(deviceIds, groupIds, from, to, daily, type.equals("mail"));
+ }
+
@Path("trips")
@GET
- @Produces(MediaType.APPLICATION_JSON)
- public Collection<TripReport> getTrips(
- @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
- @QueryParam("from") Date from, @QueryParam("to") Date to) throws SQLException {
+ public Collection<TripReportItem> getTrips(
+ @QueryParam("deviceId") List<Long> deviceIds,
+ @QueryParam("groupId") List<Long> groupIds,
+ @QueryParam("from") Date from,
+ @QueryParam("to") Date to) throws StorageException {
+ permissionsService.checkRestriction(getUserId(), UserRestrictions::getDisableReports);
LogAction.logReport(getUserId(), "trips", from, to, deviceIds, groupIds);
- return Trips.getObjects(getUserId(), deviceIds, groupIds, from, to);
+ return tripsReportProvider.getObjects(getUserId(), deviceIds, groupIds, from, to);
}
@Path("trips")
@GET
- @Produces(XLSX)
+ @Produces(EXCEL)
public Response getTripsExcel(
- @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
- @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("mail") boolean mail)
- throws SQLException, IOException {
+ @QueryParam("deviceId") List<Long> deviceIds,
+ @QueryParam("groupId") List<Long> groupIds,
+ @QueryParam("from") Date from,
+ @QueryParam("to") Date to,
+ @QueryParam("mail") boolean mail) throws StorageException {
+ permissionsService.checkRestriction(getUserId(), UserRestrictions::getDisableReports);
return executeReport(getUserId(), mail, stream -> {
LogAction.logReport(getUserId(), "trips", from, to, deviceIds, groupIds);
- Trips.getExcel(stream, getUserId(), deviceIds, groupIds, from, to);
+ tripsReportProvider.getExcel(stream, getUserId(), deviceIds, groupIds, from, to);
});
}
+ @Path("trips/{type:xlsx|mail}")
+ @GET
+ @Produces(EXCEL)
+ public Response getTripsExcel(
+ @QueryParam("deviceId") List<Long> deviceIds,
+ @QueryParam("groupId") List<Long> groupIds,
+ @QueryParam("from") Date from,
+ @QueryParam("to") Date to,
+ @PathParam("type") String type) throws StorageException {
+ return getTripsExcel(deviceIds, groupIds, from, to, type.equals("mail"));
+ }
+
@Path("stops")
@GET
- @Produces(MediaType.APPLICATION_JSON)
- public Collection<StopReport> getStops(
- @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
- @QueryParam("from") Date from, @QueryParam("to") Date to) throws SQLException {
+ public Collection<StopReportItem> getStops(
+ @QueryParam("deviceId") List<Long> deviceIds,
+ @QueryParam("groupId") List<Long> groupIds,
+ @QueryParam("from") Date from,
+ @QueryParam("to") Date to) throws StorageException {
+ permissionsService.checkRestriction(getUserId(), UserRestrictions::getDisableReports);
LogAction.logReport(getUserId(), "stops", from, to, deviceIds, groupIds);
- return Stops.getObjects(getUserId(), deviceIds, groupIds, from, to);
+ return stopsReportProvider.getObjects(getUserId(), deviceIds, groupIds, from, to);
}
@Path("stops")
@GET
- @Produces(XLSX)
+ @Produces(EXCEL)
public Response getStopsExcel(
- @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
- @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("mail") boolean mail)
- throws SQLException, IOException {
+ @QueryParam("deviceId") List<Long> deviceIds,
+ @QueryParam("groupId") List<Long> groupIds,
+ @QueryParam("from") Date from,
+ @QueryParam("to") Date to,
+ @QueryParam("mail") boolean mail) throws StorageException {
+ permissionsService.checkRestriction(getUserId(), UserRestrictions::getDisableReports);
return executeReport(getUserId(), mail, stream -> {
LogAction.logReport(getUserId(), "stops", from, to, deviceIds, groupIds);
- Stops.getExcel(stream, getUserId(), deviceIds, groupIds, from, to);
+ stopsReportProvider.getExcel(stream, getUserId(), deviceIds, groupIds, from, to);
});
}
+ @Path("stops/{type:xlsx|mail}")
+ @GET
+ @Produces(EXCEL)
+ public Response getStopsExcel(
+ @QueryParam("deviceId") List<Long> deviceIds,
+ @QueryParam("groupId") List<Long> groupIds,
+ @QueryParam("from") Date from,
+ @QueryParam("to") Date to,
+ @PathParam("type") String type) throws StorageException {
+ return getStopsExcel(deviceIds, groupIds, from, to, type.equals("mail"));
+ }
+
}
diff --git a/src/main/java/org/traccar/api/resource/ServerResource.java b/src/main/java/org/traccar/api/resource/ServerResource.java
index 91488afff..4b7ee9189 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 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,12 +15,23 @@
*/
package org.traccar.api.resource;
-import org.traccar.Context;
import org.traccar.api.BaseResource;
+import org.traccar.helper.model.UserUtil;
+import org.traccar.mail.MailManager;
+import org.traccar.geocoder.Geocoder;
+import org.traccar.helper.Log;
import org.traccar.helper.LogAction;
import org.traccar.model.Server;
+import org.traccar.model.User;
+import org.traccar.session.cache.CacheManager;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Request;
+import javax.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;
@@ -29,27 +40,52 @@ import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
-import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.TimeZone;
@Path("server")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ServerResource extends BaseResource {
+ @Inject
+ private CacheManager cacheManager;
+
+ @Inject
+ private MailManager mailManager;
+
+ @Inject
+ @Nullable
+ private Geocoder geocoder;
+
@PermitAll
@GET
- public Server get(@QueryParam("force") boolean force) throws SQLException {
- if (force) {
- return Context.getDataManager().getServer();
+ public Server get() throws StorageException {
+ Server server = storage.getObject(Server.class, new Request(new Columns.All()));
+ server.setEmailEnabled(mailManager.getEmailEnabled());
+ server.setGeocoderEnabled(geocoder != null);
+ User user = permissionsService.getUser(getUserId());
+ if (user != null) {
+ if (user.getAdministrator()) {
+ server.setStorageSpace(Log.getStorageSpace());
+ }
} else {
- return Context.getPermissionsManager().getServer();
+ server.setNewServer(UserUtil.isEmpty(storage));
+ }
+ if (user != null && user.getAdministrator()) {
+ server.setStorageSpace(Log.getStorageSpace());
}
+ return server;
}
@PUT
- public Response update(Server entity) throws SQLException {
- Context.getPermissionsManager().checkAdmin(getUserId());
- Context.getPermissionsManager().updateServer(entity);
+ public Response update(Server entity) throws StorageException {
+ permissionsService.checkAdmin(getUserId());
+ storage.updateObject(entity, new Request(
+ new Columns.Exclude("id"),
+ new Condition.Equals("id", entity.getId())));
+ cacheManager.updateOrInvalidate(true, entity);
LogAction.edit(getUserId(), entity);
return Response.ok(entity).build();
}
@@ -57,11 +93,17 @@ public class ServerResource extends BaseResource {
@Path("geocode")
@GET
public String geocode(@QueryParam("latitude") double latitude, @QueryParam("longitude") double longitude) {
- if (Context.getGeocoder() != null) {
- return Context.getGeocoder().getAddress(latitude, longitude, null);
+ if (geocoder != null) {
+ return geocoder.getAddress(latitude, longitude, null);
} else {
throw new RuntimeException("Reverse geocoding is not enabled");
}
}
+ @Path("timezones")
+ @GET
+ public Collection<String> timezones() {
+ return Arrays.asList(TimeZone.getAvailableIDs());
+ }
+
}
diff --git a/src/main/java/org/traccar/api/resource/SessionResource.java b/src/main/java/org/traccar/api/resource/SessionResource.java
index e3c5d457f..7025d5fa7 100644
--- a/src/main/java/org/traccar/api/resource/SessionResource.java
+++ b/src/main/java/org/traccar/api/resource/SessionResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,14 +15,20 @@
*/
package org.traccar.api.resource;
-import org.traccar.Context;
import org.traccar.api.BaseResource;
+import org.traccar.api.security.LoginService;
+import org.traccar.api.signature.TokenManager;
import org.traccar.helper.DataConverter;
-import org.traccar.helper.ServletHelper;
import org.traccar.helper.LogAction;
+import org.traccar.helper.ServletHelper;
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;
@@ -31,16 +37,18 @@ 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 java.io.UnsupportedEncodingException;
+import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
-import java.sql.SQLException;
+import java.security.GeneralSecurityException;
+import java.util.Date;
@Path("session")
@Produces(MediaType.APPLICATION_JSON)
@@ -51,60 +59,86 @@ public class SessionResource extends BaseResource {
public static final String USER_COOKIE_KEY = "user";
public static final String PASS_COOKIE_KEY = "password";
- @javax.ws.rs.core.Context
+ @Inject
+ private LoginService loginService;
+
+ @Inject
+ private TokenManager tokenManager;
+
+ @Context
private HttpServletRequest request;
@PermitAll
@GET
- public User get(@QueryParam("token") String token) throws SQLException, UnsupportedEncodingException {
+ public User get(@QueryParam("token") String token) throws StorageException, IOException, GeneralSecurityException {
+
+ if (token != null) {
+ User user = loginService.login(token);
+ if (user != null) {
+ request.getSession().setAttribute(USER_ID_KEY, user.getId());
+ LogAction.login(user.getId(), ServletHelper.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.name()));
+ URLDecoder.decode(cookie.getValue(), StandardCharsets.US_ASCII));
email = new String(emailBytes, StandardCharsets.UTF_8);
} else if (cookie.getName().equals(PASS_COOKIE_KEY)) {
byte[] passwordBytes = DataConverter.parseBase64(
- URLDecoder.decode(cookie.getValue(), StandardCharsets.US_ASCII.name()));
+ URLDecoder.decode(cookie.getValue(), StandardCharsets.US_ASCII));
password = new String(passwordBytes, StandardCharsets.UTF_8);
}
}
}
if (email != null && password != null) {
- User user = Context.getPermissionsManager().login(email, password);
- if (user != null) {
- userId = user.getId();
- request.getSession().setAttribute(USER_ID_KEY, userId);
- }
- } else if (token != null) {
- User user = Context.getUsersManager().getUserByToken(token);
+ User user = loginService.login(email, password);
if (user != null) {
- userId = user.getId();
- request.getSession().setAttribute(USER_ID_KEY, userId);
+ request.getSession().setAttribute(USER_ID_KEY, user.getId());
+ LogAction.login(user.getId(), ServletHelper.retrieveRemoteAddress(request));
+ return user;
}
}
- }
- if (userId != null) {
- Context.getPermissionsManager().checkUserEnabled(userId);
- return Context.getPermissionsManager().getUser(userId);
} else {
- throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build());
+
+ User user = permissionsService.getUser(userId);
+ if (user != null) {
+ return user;
+ }
+
}
+
+ throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build());
+ }
+
+ @Path("{id}")
+ @GET
+ public User get(@PathParam("id") long userId) throws StorageException {
+ permissionsService.checkAdmin(getUserId());
+ 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));
+ return user;
}
@PermitAll
@POST
public User add(
- @FormParam("email") String email, @FormParam("password") String password) throws SQLException {
- User user = Context.getPermissionsManager().login(email, password);
+ @FormParam("email") String email, @FormParam("password") String password) throws StorageException {
+ User user = loginService.login(email, password);
if (user != null) {
request.getSession().setAttribute(USER_ID_KEY, user.getId());
- LogAction.login(user.getId());
+ LogAction.login(user.getId(), ServletHelper.retrieveRemoteAddress(request));
return user;
} else {
LogAction.failedLogin(ServletHelper.retrieveRemoteAddress(request));
@@ -114,9 +148,16 @@ public class SessionResource extends BaseResource {
@DELETE
public Response remove() {
- LogAction.logout(getUserId());
+ LogAction.logout(getUserId(), ServletHelper.retrieveRemoteAddress(request));
request.getSession().removeAttribute(USER_ID_KEY);
return Response.noContent().build();
}
+ @Path("token")
+ @POST
+ public String requestToken(
+ @FormParam("expiration") Date expiration) throws StorageException, GeneralSecurityException, IOException {
+ return tokenManager.generateToken(getUserId(), expiration);
+ }
+
}
diff --git a/src/main/java/org/traccar/api/resource/StatisticsResource.java b/src/main/java/org/traccar/api/resource/StatisticsResource.java
index 58073e7d1..1f2296f28 100644
--- a/src/main/java/org/traccar/api/resource/StatisticsResource.java
+++ b/src/main/java/org/traccar/api/resource/StatisticsResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,9 +15,13 @@
*/
package org.traccar.api.resource;
-import org.traccar.Context;
import org.traccar.api.BaseResource;
import org.traccar.model.Statistics;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Order;
+import org.traccar.storage.query.Request;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
@@ -25,7 +29,6 @@ import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
-import java.sql.SQLException;
import java.util.Collection;
import java.util.Date;
@@ -36,9 +39,12 @@ public class StatisticsResource extends BaseResource {
@GET
public Collection<Statistics> get(
- @QueryParam("from") Date from, @QueryParam("to") Date to) throws SQLException {
- Context.getPermissionsManager().checkAdmin(getUserId());
- return Context.getDataManager().getStatistics(from, to);
+ @QueryParam("from") Date from, @QueryParam("to") Date to) throws StorageException {
+ permissionsService.checkAdmin(getUserId());
+ return storage.getObjects(Statistics.class, new Request(
+ new Columns.All(),
+ new Condition.Between("captureTime", "from", from, "to", to),
+ new Order("captureTime")));
}
}
diff --git a/src/main/java/org/traccar/api/resource/UserResource.java b/src/main/java/org/traccar/api/resource/UserResource.java
index d54cc2382..e41ebbe61 100644
--- a/src/main/java/org/traccar/api/resource/UserResource.java
+++ b/src/main/java/org/traccar/api/resource/UserResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,15 +15,21 @@
*/
package org.traccar.api.resource;
-import org.traccar.Context;
import org.traccar.api.BaseObjectResource;
+import org.traccar.config.Config;
import org.traccar.config.Keys;
-import org.traccar.database.UsersManager;
import org.traccar.helper.LogAction;
+import org.traccar.helper.model.UserUtil;
import org.traccar.model.ManagedUser;
+import org.traccar.model.Permission;
import org.traccar.model.User;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Request;
import javax.annotation.security.PermitAll;
+import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
@@ -32,63 +38,82 @@ import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
-import java.sql.SQLException;
import java.util.Collection;
import java.util.Date;
-import java.util.Set;
@Path("users")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class UserResource extends BaseObjectResource<User> {
+ @Inject
+ private Config config;
+
public UserResource() {
super(User.class);
}
@GET
- public Collection<User> get(@QueryParam("userId") long userId) throws SQLException {
- UsersManager usersManager = Context.getUsersManager();
- Set<Long> result;
- if (Context.getPermissionsManager().getUserAdmin(getUserId())) {
- if (userId != 0) {
- result = usersManager.getUserItems(userId);
- } else {
- result = usersManager.getAllItems();
- }
- } else if (Context.getPermissionsManager().getUserManager(getUserId())) {
- result = usersManager.getManagedItems(getUserId());
+ public Collection<User> get(@QueryParam("userId") long userId) throws StorageException {
+ if (userId > 0) {
+ permissionsService.checkUser(getUserId(), userId);
+ return storage.getObjects(baseClass, new Request(
+ new Columns.All(),
+ new Condition.Permission(User.class, userId, ManagedUser.class).excludeGroups()));
+ } else if (permissionsService.notAdmin(getUserId())) {
+ return storage.getObjects(baseClass, new Request(
+ new Columns.All(),
+ new Condition.Permission(User.class, getUserId(), ManagedUser.class).excludeGroups()));
} else {
- throw new SecurityException("Admin or manager access required");
+ return storage.getObjects(baseClass, new Request(new Columns.All()));
}
- return usersManager.getItems(result);
}
@Override
@PermitAll
@POST
- public Response add(User entity) throws SQLException {
- if (!Context.getPermissionsManager().getUserAdmin(getUserId())) {
- Context.getPermissionsManager().checkUserUpdate(getUserId(), new User(), entity);
- if (Context.getPermissionsManager().getUserManager(getUserId())) {
- Context.getPermissionsManager().checkUserLimit(getUserId());
+ public Response add(User entity) throws StorageException {
+ User currentUser = getUserId() > 0 ? permissionsService.getUser(getUserId()) : null;
+ if (currentUser == null || !currentUser.getAdministrator()) {
+ permissionsService.checkUserUpdate(getUserId(), new User(), entity);
+ if (currentUser != null && currentUser.getUserLimit() != 0) {
+ int userLimit = currentUser.getUserLimit();
+ if (userLimit > 0) {
+ int userCount = storage.getObjects(baseClass, new Request(
+ new Columns.All(),
+ new Condition.Permission(User.class, getUserId(), ManagedUser.class).excludeGroups()))
+ .size();
+ if (userCount >= userLimit) {
+ throw new SecurityException("Manager user limit reached");
+ }
+ }
} else {
- Context.getPermissionsManager().checkRegistration(getUserId());
- entity.setDeviceLimit(Context.getConfig().getInteger(Keys.USERS_DEFAULT_DEVICE_LIMIT));
- int expirationDays = Context.getConfig().getInteger(Keys.USERS_DEFAULT_EXPIRATION_DAYS);
+ if (!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() + (long) expirationDays * 24 * 3600 * 1000));
+ entity.setExpirationTime(new Date(System.currentTimeMillis() + expirationDays * 86400000L));
}
}
}
- Context.getUsersManager().addItem(entity);
+
+ if (UserUtil.isEmpty(storage)) {
+ entity.setAdministrator(true);
+ }
+
+ entity.setId(storage.addObject(entity, new Request(new Columns.Exclude("id"))));
+ storage.updateObject(entity, new Request(
+ new Columns.Include("hashedPassword", "salt"),
+ new Condition.Equals("id", entity.getId())));
+
LogAction.create(getUserId(), entity);
- if (Context.getPermissionsManager().getUserManager(getUserId())) {
- Context.getDataManager().linkObject(User.class, getUserId(), ManagedUser.class, entity.getId(), true);
+
+ if (currentUser != null && currentUser.getUserLimit() != 0) {
+ storage.addPermission(new Permission(User.class, getUserId(), ManagedUser.class, entity.getId()));
LogAction.link(getUserId(), User.class, getUserId(), ManagedUser.class, entity.getId());
}
- Context.getUsersManager().refreshUserItems();
return Response.ok(entity).build();
}
diff --git a/src/main/java/org/traccar/api/security/LoginService.java b/src/main/java/org/traccar/api/security/LoginService.java
new file mode 100644
index 000000000..88bafcfb5
--- /dev/null
+++ b/src/main/java/org/traccar/api/security/LoginService.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.api.security;
+
+import org.traccar.api.signature.TokenManager;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.database.LdapProvider;
+import org.traccar.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 javax.annotation.Nullable;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+
+@Singleton
+public class LoginService {
+
+ private final Storage storage;
+ private final TokenManager tokenManager;
+ private final LdapProvider ldapProvider;
+
+ private final String serviceAccountToken;
+ private final boolean forceLdap;
+
+ @Inject
+ public LoginService(
+ Config config, Storage storage, TokenManager tokenManager, @Nullable LdapProvider ldapProvider) {
+ this.storage = storage;
+ this.tokenManager = tokenManager;
+ this.ldapProvider = ldapProvider;
+ serviceAccountToken = config.getString(Keys.WEB_SERVICE_ACCOUNT_TOKEN);
+ forceLdap = config.getBoolean(Keys.LDAP_FORCE);
+ }
+
+ public User login(String token) throws StorageException, GeneralSecurityException, IOException {
+ if (serviceAccountToken != null && serviceAccountToken.equals(token)) {
+ return new ServiceAccountUser();
+ }
+ long userId = tokenManager.verifyToken(token);
+ User user = storage.getObject(User.class, new Request(
+ new Columns.All(), new Condition.Equals("id", userId)));
+ if (user != null) {
+ checkUserEnabled(user);
+ }
+ return user;
+ }
+
+ public User login(String email, String password) throws StorageException {
+ email = email.trim();
+ User user = storage.getObject(User.class, new Request(
+ new Columns.All(),
+ new Condition.Or(
+ new Condition.Equals("email", email),
+ new Condition.Equals("login", email))));
+ if (user != null) {
+ if (ldapProvider != null && user.getLogin() != null && ldapProvider.login(user.getLogin(), password)
+ || !forceLdap && user.isPasswordValid(password)) {
+ checkUserEnabled(user);
+ return user;
+ }
+ } else {
+ if (ldapProvider != null && ldapProvider.login(email, password)) {
+ user = ldapProvider.getUser(email);
+ user.setId(storage.addObject(user, new Request(new Columns.Exclude("id"))));
+ checkUserEnabled(user);
+ return user;
+ }
+ }
+ return null;
+ }
+
+ private void checkUserEnabled(User user) throws SecurityException {
+ if (user == null) {
+ throw new SecurityException("Unknown account");
+ }
+ user.checkDisabled();
+ }
+
+}
diff --git a/src/main/java/org/traccar/api/security/PermissionsService.java b/src/main/java/org/traccar/api/security/PermissionsService.java
new file mode 100644
index 000000000..4421572d7
--- /dev/null
+++ b/src/main/java/org/traccar/api/security/PermissionsService.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.api.security;
+
+import com.google.inject.servlet.RequestScoped;
+import org.traccar.model.BaseModel;
+import org.traccar.model.Calendar;
+import org.traccar.model.Command;
+import org.traccar.model.Device;
+import org.traccar.model.Group;
+import org.traccar.model.GroupedModel;
+import org.traccar.model.ManagedUser;
+import org.traccar.model.ScheduledModel;
+import org.traccar.model.Server;
+import org.traccar.model.User;
+import org.traccar.model.UserRestrictions;
+import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Request;
+
+import javax.inject.Inject;
+import java.util.Objects;
+
+@RequestScoped
+public class PermissionsService {
+
+ private final Storage storage;
+
+ private Server server;
+ private User user;
+
+ @Inject
+ public PermissionsService(Storage storage) {
+ this.storage = storage;
+ }
+
+ public Server getServer() throws StorageException {
+ if (server == null) {
+ server = storage.getObject(
+ Server.class, new Request(new Columns.All()));
+ }
+ return server;
+ }
+
+ public User getUser(long userId) throws StorageException {
+ if (user == null && userId > 0) {
+ if (userId == ServiceAccountUser.ID) {
+ user = new ServiceAccountUser();
+ } else {
+ user = storage.getObject(
+ User.class, new Request(new Columns.All(), new Condition.Equals("id", userId)));
+ }
+ }
+ return user;
+ }
+
+ public boolean notAdmin(long userId) throws StorageException {
+ return !getUser(userId).getAdministrator();
+ }
+
+ public void checkAdmin(long userId) throws StorageException, SecurityException {
+ if (!getUser(userId).getAdministrator()) {
+ throw new SecurityException("Administrator access required");
+ }
+ }
+
+ public void checkManager(long userId) throws StorageException, SecurityException {
+ if (!getUser(userId).getAdministrator() && getUser(userId).getUserLimit() == 0) {
+ throw new SecurityException("Manager access required");
+ }
+ }
+
+ public interface CheckRestrictionCallback {
+ boolean denied(UserRestrictions userRestrictions);
+ }
+
+ public void checkRestriction(
+ long userId, CheckRestrictionCallback callback) throws StorageException, SecurityException {
+ if (!getUser(userId).getAdministrator()
+ && (callback.denied(getServer()) || callback.denied(getUser(userId)))) {
+ throw new SecurityException("Operation restricted");
+ }
+ }
+
+ public void checkEdit(long userId, Class<?> clazz, boolean addition) throws StorageException, SecurityException {
+ if (!getUser(userId).getAdministrator()) {
+ boolean denied = false;
+ if (getServer().getReadonly() || getUser(userId).getReadonly()) {
+ denied = true;
+ } else if (clazz.equals(Device.class)) {
+ denied = getServer().getDeviceReadonly() || getUser(userId).getDeviceReadonly()
+ || addition && getUser(userId).getDeviceLimit() == 0;
+ if (!denied && addition && getUser(userId).getDeviceLimit() > 0) {
+ int deviceCount = storage.getObjects(Device.class, new Request(
+ new Columns.Include("id"),
+ new Condition.Permission(User.class, userId, Device.class))).size();
+ denied = deviceCount >= getUser(userId).getDeviceLimit();
+ }
+ } else if (clazz.equals(Command.class)) {
+ denied = getServer().getLimitCommands() || getUser(userId).getLimitCommands();
+ }
+ if (denied) {
+ throw new SecurityException("Write access denied");
+ }
+ }
+ }
+
+ public void checkEdit(long userId, BaseModel object, boolean addition) throws StorageException, SecurityException {
+ if (!getUser(userId).getAdministrator()) {
+ checkEdit(userId, object.getClass(), addition);
+ if (object instanceof GroupedModel) {
+ GroupedModel after = ((GroupedModel) object);
+ if (after.getGroupId() > 0) {
+ GroupedModel before = null;
+ if (!addition) {
+ before = storage.getObject(after.getClass(), new Request(
+ new Columns.Include("groupId"), new Condition.Equals("id", object.getId())));
+ }
+ if (before == null || before.getGroupId() != after.getGroupId()) {
+ checkPermission(Group.class, userId, after.getGroupId());
+ }
+ }
+ }
+ if (object instanceof ScheduledModel) {
+ ScheduledModel after = ((ScheduledModel) object);
+ if (after.getCalendarId() > 0) {
+ ScheduledModel before = null;
+ if (!addition) {
+ before = storage.getObject(after.getClass(), new Request(
+ new Columns.Include("calendarId"), new Condition.Equals("id", object.getId())));
+ }
+ if (before == null || before.getCalendarId() != after.getCalendarId()) {
+ checkPermission(Calendar.class, userId, after.getCalendarId());
+ }
+ }
+ }
+ }
+ }
+
+ public void checkUser(long userId, long managedUserId) throws StorageException, SecurityException {
+ if (userId != managedUserId && !getUser(userId).getAdministrator()) {
+ if (!getUser(userId).getManager()
+ || storage.getPermissions(User.class, userId, ManagedUser.class, managedUserId).isEmpty()) {
+ throw new SecurityException("User access denied");
+ }
+ }
+ }
+
+ public void checkUserUpdate(long userId, User before, User after) throws StorageException, SecurityException {
+ if (before.getAdministrator() != after.getAdministrator()
+ || before.getDeviceLimit() != after.getDeviceLimit()
+ || before.getUserLimit() != after.getUserLimit()) {
+ checkAdmin(userId);
+ }
+ User user = getUser(userId);
+ if (user != null && user.getExpirationTime() != null
+ && !Objects.equals(before.getExpirationTime(), after.getExpirationTime())
+ && (after.getExpirationTime() == null
+ || user.getExpirationTime().compareTo(after.getExpirationTime()) < 0)) {
+ checkAdmin(userId);
+ }
+ if (before.getReadonly() != after.getReadonly()
+ || before.getDeviceReadonly() != after.getDeviceReadonly()
+ || before.getDisabled() != after.getDisabled()
+ || before.getLimitCommands() != after.getLimitCommands()
+ || before.getDisableReports() != after.getDisableReports()
+ || before.getFixedEmail() != after.getFixedEmail()) {
+ if (userId == after.getId()) {
+ checkAdmin(userId);
+ } else if (after.getId() > 0) {
+ checkUser(userId, after.getId());
+ } else {
+ checkManager(userId);
+ }
+ }
+ if (before.getFixedEmail() && !before.getEmail().equals(after.getEmail())) {
+ checkAdmin(userId);
+ }
+ }
+
+ public <T extends BaseModel> void checkPermission(
+ Class<T> clazz, long userId, long objectId) throws StorageException, SecurityException {
+ if (!getUser(userId).getAdministrator() && !(clazz.equals(User.class) && userId == objectId)) {
+ var object = storage.getObject(clazz, new Request(
+ new Columns.Include("id"),
+ new Condition.And(
+ new Condition.Equals("id", objectId),
+ new Condition.Permission(
+ User.class, userId, clazz.equals(User.class) ? ManagedUser.class : clazz))));
+ if (object == null) {
+ throw new SecurityException(clazz.getSimpleName() + " access denied");
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/api/SecurityRequestFilter.java b/src/main/java/org/traccar/api/security/SecurityRequestFilter.java
index 33b6b37df..94b6bbf05 100644
--- a/src/main/java/org/traccar/api/SecurityRequestFilter.java
+++ b/src/main/java/org/traccar/api/security/SecurityRequestFilter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,28 +13,30 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.api;
+package org.traccar.api.security;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.traccar.Context;
-import org.traccar.Main;
import org.traccar.api.resource.SessionResource;
import org.traccar.database.StatisticsManager;
import org.traccar.helper.DataConverter;
import org.traccar.model.User;
+import org.traccar.storage.StorageException;
import javax.annotation.security.PermitAll;
+import javax.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 java.io.IOException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
-import java.sql.SQLException;
+import java.security.GeneralSecurityException;
public class SecurityRequestFilter implements ContainerRequestFilter {
@@ -43,6 +45,7 @@ public class SecurityRequestFilter implements ContainerRequestFilter {
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";
@@ -55,12 +58,18 @@ public class SecurityRequestFilter implements ContainerRequestFilter {
return null;
}
- @javax.ws.rs.core.Context
+ @Context
private HttpServletRequest request;
- @javax.ws.rs.core.Context
+ @Context
private ResourceInfo resourceInfo;
+ @Inject
+ private LoginService loginService;
+
+ @Inject
+ private StatisticsManager statisticsManager;
+
@Override
public void filter(ContainerRequestContext requestContext) {
@@ -76,13 +85,18 @@ public class SecurityRequestFilter implements ContainerRequestFilter {
if (authHeader != null) {
try {
- String[] auth = decodeBasicAuth(authHeader);
- User user = Context.getPermissionsManager().login(auth[0], auth[1]);
+ User user;
+ if (authHeader.startsWith(BEARER_PREFIX)) {
+ user = loginService.login(authHeader.substring(BEARER_PREFIX.length()));
+ } else {
+ String[] auth = decodeBasicAuth(authHeader);
+ user = loginService.login(auth[0], auth[1]);
+ }
if (user != null) {
- Main.getInjector().getInstance(StatisticsManager.class).registerRequest(user.getId());
+ statisticsManager.registerRequest(user.getId());
securityContext = new UserSecurityContext(new UserPrincipal(user.getId()));
}
- } catch (SQLException e) {
+ } catch (StorageException | GeneralSecurityException | IOException e) {
throw new WebApplicationException(e);
}
@@ -90,8 +104,7 @@ public class SecurityRequestFilter implements ContainerRequestFilter {
Long userId = (Long) request.getSession().getAttribute(SessionResource.USER_ID_KEY);
if (userId != null) {
- Context.getPermissionsManager().checkUserEnabled(userId);
- Main.getInjector().getInstance(StatisticsManager.class).registerRequest(userId);
+ statisticsManager.registerRequest(userId);
securityContext = new UserSecurityContext(new UserPrincipal(userId));
}
diff --git a/src/main/java/org/traccar/database/MaintenancesManager.java b/src/main/java/org/traccar/api/security/ServiceAccountUser.java
index 4e266cb78..644142434 100644
--- a/src/main/java/org/traccar/database/MaintenancesManager.java
+++ b/src/main/java/org/traccar/api/security/ServiceAccountUser.java
@@ -1,6 +1,5 @@
/*
- * Copyright 2018 Anton Tananaev (anton@traccar.org)
- * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.database;
+package org.traccar.api.security;
-import org.traccar.model.Maintenance;
+import org.traccar.model.User;
-public class MaintenancesManager extends ExtendedObjectManager<Maintenance> {
+public class ServiceAccountUser extends User {
- public MaintenancesManager(DataManager dataManager) {
- super(dataManager, Maintenance.class);
- }
+ public static final long ID = 9000000000000000000L;
+ public ServiceAccountUser() {
+ setId(ID);
+ setName("Service Account");
+ setEmail("none");
+ setAdministrator(true);
+ }
}
diff --git a/src/main/java/org/traccar/api/UserPrincipal.java b/src/main/java/org/traccar/api/security/UserPrincipal.java
index 175bca391..18b84a0e1 100644
--- a/src/main/java/org/traccar/api/UserPrincipal.java
+++ b/src/main/java/org/traccar/api/security/UserPrincipal.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.api;
+package org.traccar.api.security;
import java.security.Principal;
diff --git a/src/main/java/org/traccar/api/UserSecurityContext.java b/src/main/java/org/traccar/api/security/UserSecurityContext.java
index 55c0621bc..97df6b6c7 100644
--- a/src/main/java/org/traccar/api/UserSecurityContext.java
+++ b/src/main/java/org/traccar/api/security/UserSecurityContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,14 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.api;
+package org.traccar.api.security;
import javax.ws.rs.core.SecurityContext;
import java.security.Principal;
public class UserSecurityContext implements SecurityContext {
- private UserPrincipal principal;
+ private final UserPrincipal principal;
public UserSecurityContext(UserPrincipal principal) {
this.principal = principal;
diff --git a/src/main/java/org/traccar/api/signature/CryptoManager.java b/src/main/java/org/traccar/api/signature/CryptoManager.java
new file mode 100644
index 000000000..249d5bd97
--- /dev/null
+++ b/src/main/java/org/traccar/api/signature/CryptoManager.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.api.signature;
+
+import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Request;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.Signature;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+@Singleton
+public class CryptoManager {
+
+ private final Storage storage;
+
+ private PublicKey publicKey;
+ private PrivateKey privateKey;
+
+ @Inject
+ public CryptoManager(Storage storage) {
+ this.storage = storage;
+ }
+
+ public byte[] sign(byte[] data) throws GeneralSecurityException, StorageException {
+ if (privateKey == null) {
+ initializeKeys();
+ }
+ Signature signature = Signature.getInstance("SHA256withECDSA");
+ signature.initSign(privateKey);
+ signature.update(data);
+ byte[] block = signature.sign();
+ byte[] combined = new byte[1 + block.length + data.length];
+ combined[0] = (byte) block.length;
+ System.arraycopy(block, 0, combined, 1, block.length);
+ System.arraycopy(data, 0, combined, 1 + block.length, data.length);
+ return combined;
+ }
+
+ public byte[] verify(byte[] data) throws GeneralSecurityException, StorageException {
+ if (publicKey == null) {
+ initializeKeys();
+ }
+ Signature signature = Signature.getInstance("SHA256withECDSA");
+ signature.initVerify(publicKey);
+ int length = data[0];
+ byte[] originalData = new byte[data.length - 1 - length];
+ System.arraycopy(data, 1 + length, originalData, 0, originalData.length);
+ signature.update(originalData);
+ if (!signature.verify(data, 1, length)) {
+ throw new SecurityException("Invalid signature");
+ }
+ return originalData;
+ }
+
+ private void initializeKeys() throws StorageException, GeneralSecurityException {
+ KeystoreModel model = storage.getObject(KeystoreModel.class, new Request(new Columns.All()));
+ if (model != null) {
+ publicKey = KeyFactory.getInstance("EC")
+ .generatePublic(new X509EncodedKeySpec(model.getPublicKey()));
+ privateKey = KeyFactory.getInstance("EC")
+ .generatePrivate(new PKCS8EncodedKeySpec(model.getPrivateKey()));
+ } else {
+ KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
+ generator.initialize(new ECGenParameterSpec("secp256r1"), new SecureRandom());
+ KeyPair pair = generator.generateKeyPair();
+
+ publicKey = pair.getPublic();
+ privateKey = pair.getPrivate();
+
+ model = new KeystoreModel();
+ model.setPublicKey(publicKey.getEncoded());
+ model.setPrivateKey(privateKey.getEncoded());
+ storage.addObject(model, new Request(new Columns.Exclude("id")));
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/api/signature/KeystoreModel.java b/src/main/java/org/traccar/api/signature/KeystoreModel.java
new file mode 100644
index 000000000..7f3140e81
--- /dev/null
+++ b/src/main/java/org/traccar/api/signature/KeystoreModel.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.api.signature;
+
+import org.traccar.model.BaseModel;
+import org.traccar.storage.StorageName;
+
+@StorageName("tc_keystore")
+public class KeystoreModel extends BaseModel {
+
+ private byte[] publicKey;
+
+ public byte[] getPublicKey() {
+ return publicKey;
+ }
+
+ public void setPublicKey(byte[] publicKey) {
+ this.publicKey = publicKey;
+ }
+
+ private byte[] privateKey;
+
+ public byte[] getPrivateKey() {
+ return privateKey;
+ }
+
+ public void setPrivateKey(byte[] privateKey) {
+ this.privateKey = privateKey;
+ }
+
+}
diff --git a/src/main/java/org/traccar/api/signature/TokenManager.java b/src/main/java/org/traccar/api/signature/TokenManager.java
new file mode 100644
index 000000000..6a0d90b40
--- /dev/null
+++ b/src/main/java/org/traccar/api/signature/TokenManager.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.api.signature;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.codec.binary.Base64;
+import org.traccar.storage.StorageException;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+
+@Singleton
+public class TokenManager {
+
+ private static final int DEFAULT_EXPIRATION_DAYS = 7;
+
+ private final ObjectMapper objectMapper;
+ private final CryptoManager cryptoManager;
+
+ public static class Data {
+ @JsonProperty("u")
+ private long userId;
+ @JsonProperty("e")
+ private Date expiration;
+ }
+
+ @Inject
+ public TokenManager(ObjectMapper objectMapper, CryptoManager cryptoManager) {
+ this.objectMapper = objectMapper;
+ this.cryptoManager = cryptoManager;
+ }
+
+ public String generateToken(long userId) throws IOException, GeneralSecurityException, StorageException {
+ return generateToken(userId, null);
+ }
+
+ public String generateToken(
+ long userId, Date expiration) throws IOException, GeneralSecurityException, StorageException {
+ Data data = new Data();
+ data.userId = userId;
+ if (expiration != null) {
+ data.expiration = expiration;
+ } else {
+ data.expiration = new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(DEFAULT_EXPIRATION_DAYS));
+ }
+ byte[] encoded = objectMapper.writeValueAsBytes(data);
+ return Base64.encodeBase64URLSafeString(cryptoManager.sign(encoded));
+ }
+
+ public long verifyToken(String token) throws IOException, GeneralSecurityException, StorageException {
+ byte[] encoded = cryptoManager.verify(Base64.decodeBase64(token));
+ Data data = objectMapper.readValue(encoded, Data.class);
+ if (data.expiration.before(new Date())) {
+ throw new SecurityException("Token has expired");
+ }
+ return data.userId;
+ }
+
+}
diff --git a/src/main/java/org/traccar/broadcast/BroadcastInterface.java b/src/main/java/org/traccar/broadcast/BroadcastInterface.java
new file mode 100644
index 000000000..673ebd8b8
--- /dev/null
+++ b/src/main/java/org/traccar/broadcast/BroadcastInterface.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.broadcast;
+
+import org.traccar.model.BaseModel;
+import org.traccar.model.Device;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+
+public interface BroadcastInterface {
+
+ default void updateDevice(boolean local, Device device) {
+ }
+
+ default void updatePosition(boolean local, Position position) {
+ }
+
+ default void updateEvent(boolean local, long userId, Event event) {
+ }
+
+ default void updateCommand(boolean local, long deviceId) {
+ }
+
+ default void invalidateObject(boolean local, Class<? extends BaseModel> clazz, long id) {
+ }
+
+ default void invalidatePermission(
+ boolean local,
+ Class<? extends BaseModel> clazz1, long id1,
+ Class<? extends BaseModel> clazz2, long id2) {
+ }
+}
diff --git a/src/main/java/org/traccar/broadcast/BroadcastMessage.java b/src/main/java/org/traccar/broadcast/BroadcastMessage.java
new file mode 100644
index 000000000..985848d04
--- /dev/null
+++ b/src/main/java/org/traccar/broadcast/BroadcastMessage.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.broadcast;
+
+import org.traccar.model.Device;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+
+import java.util.Map;
+
+public class BroadcastMessage {
+
+ private Device device;
+
+ public Device getDevice() {
+ return device;
+ }
+
+ public void setDevice(Device device) {
+ this.device = device;
+ }
+
+ private Position position;
+
+ public Position getPosition() {
+ return position;
+ }
+
+ public void setPosition(Position position) {
+ this.position = position;
+ }
+
+ private Long userId;
+
+ public Long getUserId() {
+ return userId;
+ }
+
+ public void setUserId(Long userId) {
+ this.userId = userId;
+ }
+
+ private Event event;
+
+ public Event getEvent() {
+ return event;
+ }
+
+ public void setEvent(Event event) {
+ this.event = event;
+ }
+
+ private Long commandDeviceId;
+
+ public Long getCommandDeviceId() {
+ return commandDeviceId;
+ }
+
+ public void setCommandDeviceId(Long commandDeviceId) {
+ this.commandDeviceId = commandDeviceId;
+ }
+
+ private Map<String, Long> changes;
+
+ public Map<String, Long> getChanges() {
+ return changes;
+ }
+
+ public void setChanges(Map<String, Long> changes) {
+ this.changes = changes;
+ }
+}
diff --git a/src/main/java/org/traccar/broadcast/BroadcastService.java b/src/main/java/org/traccar/broadcast/BroadcastService.java
new file mode 100644
index 000000000..a86c43b5b
--- /dev/null
+++ b/src/main/java/org/traccar/broadcast/BroadcastService.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.broadcast;
+
+import org.traccar.LifecycleObject;
+
+public interface BroadcastService extends LifecycleObject, BroadcastInterface {
+ boolean singleInstance();
+ void registerListener(BroadcastInterface listener);
+}
diff --git a/src/main/java/org/traccar/broadcast/MulticastBroadcastService.java b/src/main/java/org/traccar/broadcast/MulticastBroadcastService.java
new file mode 100644
index 000000000..b1b66f1e3
--- /dev/null
+++ b/src/main/java/org/traccar/broadcast/MulticastBroadcastService.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.broadcast;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import 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;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.MulticastSocket;
+import java.net.NetworkInterface;
+import java.nio.charset.StandardCharsets;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class MulticastBroadcastService implements BroadcastService {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(MulticastBroadcastService.class);
+
+ private final ObjectMapper objectMapper;
+
+ private final NetworkInterface networkInterface;
+ private final int port;
+ private final InetSocketAddress group;
+
+ private DatagramSocket publisherSocket;
+
+ private final ExecutorService service = Executors.newSingleThreadExecutor();
+ private final byte[] receiverBuffer = new byte[4096];
+
+ private final Set<BroadcastInterface> listeners = new HashSet<>();
+
+ public MulticastBroadcastService(Config config, ObjectMapper objectMapper) throws IOException {
+ this.objectMapper = objectMapper;
+ port = config.getInteger(Keys.BROADCAST_PORT);
+ String interfaceName = config.getString(Keys.BROADCAST_INTERFACE);
+ if (interfaceName.indexOf('.') >= 0 || interfaceName.indexOf(':') >= 0) {
+ networkInterface = NetworkInterface.getByInetAddress(InetAddress.getByName(interfaceName));
+ } else {
+ networkInterface = NetworkInterface.getByName(interfaceName);
+ }
+ InetAddress address = InetAddress.getByName(config.getString(Keys.BROADCAST_ADDRESS));
+ group = new InetSocketAddress(address, port);
+ }
+
+ @Override
+ public boolean singleInstance() {
+ return false;
+ }
+
+ @Override
+ 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) {
+ try {
+ byte[] buffer = objectMapper.writeValueAsString(message).getBytes(StandardCharsets.UTF_8);
+ DatagramPacket packet = new DatagramPacket(buffer, buffer.length, group);
+ publisherSocket.send(packet);
+ } catch (IOException e) {
+ LOGGER.warn("Broadcast failed", e);
+ }
+ }
+
+ 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);
+ }
+
+ @Override
+ public void stop() {
+ service.shutdown();
+ }
+
+ private final Runnable receiver = new Runnable() {
+ @Override
+ public void run() {
+ try (MulticastSocket socket = new MulticastSocket(port)) {
+ socket.setNetworkInterface(networkInterface);
+ socket.joinGroup(group, networkInterface);
+ publisherSocket = socket;
+ while (!service.isShutdown()) {
+ DatagramPacket packet = new DatagramPacket(receiverBuffer, receiverBuffer.length);
+ socket.receive(packet);
+ if (networkInterface.inetAddresses().noneMatch(a -> a.equals(packet.getAddress()))) {
+ String data = new String(packet.getData(), 0, packet.getLength(), StandardCharsets.UTF_8);
+ handleMessage(objectMapper.readValue(data, BroadcastMessage.class));
+ }
+ }
+ publisherSocket = null;
+ socket.leaveGroup(group, networkInterface);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+
+}
diff --git a/src/main/java/org/traccar/broadcast/NullBroadcastService.java b/src/main/java/org/traccar/broadcast/NullBroadcastService.java
new file mode 100644
index 000000000..f95037990
--- /dev/null
+++ b/src/main/java/org/traccar/broadcast/NullBroadcastService.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.broadcast;
+
+public class NullBroadcastService implements BroadcastService {
+
+ @Override
+ public boolean singleInstance() {
+ return true;
+ }
+
+ @Override
+ public void registerListener(BroadcastInterface listener) {
+ }
+
+ @Override
+ public void start() throws Exception {
+ }
+
+ @Override
+ public void stop() throws Exception {
+ }
+}
diff --git a/src/main/java/org/traccar/config/Config.java b/src/main/java/org/traccar/config/Config.java
index 815a6e86a..c73be6475 100644
--- a/src/main/java/org/traccar/config/Config.java
+++ b/src/main/java/org/traccar/config/Config.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,13 +16,19 @@
package org.traccar.config;
import com.google.common.annotations.VisibleForTesting;
+import com.google.inject.name.Named;
+import org.traccar.helper.Log;
+import javax.inject.Inject;
+import javax.inject.Singleton;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.InvalidPropertiesFormatException;
+import java.util.Objects;
import java.util.Properties;
+@Singleton
public class Config {
private final Properties properties = new Properties();
@@ -32,7 +38,8 @@ public class Config {
public Config() {
}
- public Config(String file) throws IOException {
+ @Inject
+ public Config(@Named("configFile") String file) throws IOException {
try {
Properties mainProperties = new Properties();
try (InputStream inputStream = new FileInputStream(file)) {
@@ -50,8 +57,14 @@ public class Config {
useEnvironmentVariables = Boolean.parseBoolean(System.getenv("CONFIG_USE_ENVIRONMENT_VARIABLES"))
|| Boolean.parseBoolean(properties.getProperty("config.useEnvironmentVariables"));
+
+ Log.setupLogger(this);
} catch (InvalidPropertiesFormatException e) {
+ Log.setupDefaultLogger();
throw new RuntimeException("Configuration file is not a valid XML document", e);
+ } catch (Exception e) {
+ Log.setupDefaultLogger();
+ throw e;
}
}
@@ -59,8 +72,7 @@ public class Config {
return hasKey(key.getKey());
}
- @Deprecated
- public boolean hasKey(String key) {
+ private boolean hasKey(String key) {
return useEnvironmentVariables && System.getenv().containsKey(getEnvironmentVariableName(key))
|| properties.containsKey(key);
}
@@ -90,12 +102,7 @@ public class Config {
}
public boolean getBoolean(ConfigKey<Boolean> key) {
- return getBoolean(key.getKey());
- }
-
- @Deprecated
- public boolean getBoolean(String key) {
- return Boolean.parseBoolean(getString(key));
+ return Boolean.parseBoolean(getString(key.getKey()));
}
public int getInteger(ConfigKey<Integer> key) {
@@ -104,11 +111,7 @@ public class Config {
return Integer.parseInt(value);
} else {
Integer defaultValue = key.getDefaultValue();
- if (defaultValue != null) {
- return defaultValue;
- } else {
- return 0;
- }
+ return Objects.requireNonNullElse(defaultValue, 0);
}
}
@@ -127,11 +130,7 @@ public class Config {
return Long.parseLong(value);
} else {
Long defaultValue = key.getDefaultValue();
- if (defaultValue != null) {
- return defaultValue;
- } else {
- return 0;
- }
+ return Objects.requireNonNullElse(defaultValue, 0L);
}
}
@@ -141,11 +140,7 @@ public class Config {
return Double.parseDouble(value);
} else {
Double defaultValue = key.getDefaultValue();
- if (defaultValue != null) {
- return defaultValue;
- } else {
- return 0;
- }
+ return Objects.requireNonNullElse(defaultValue, 0.0);
}
}
diff --git a/src/main/java/org/traccar/config/ConfigKey.java b/src/main/java/org/traccar/config/ConfigKey.java
index c046a46a5..b8151392c 100644
--- a/src/main/java/org/traccar/config/ConfigKey.java
+++ b/src/main/java/org/traccar/config/ConfigKey.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,30 +15,34 @@
*/
package org.traccar.config;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
-public class ConfigKey<T> {
+public abstract class ConfigKey<T> {
private final String key;
- private final List<KeyType> types;
+ private final Set<KeyType> types = new HashSet<>();
+ private final Class<T> valueClass;
private final T defaultValue;
- ConfigKey(String key, List<KeyType> types) {
- this(key, types, null);
- }
-
- ConfigKey(String key, List<KeyType> types, T defaultValue) {
+ ConfigKey(String key, List<KeyType> types, Class<T> valueClass, T defaultValue) {
this.key = key;
- this.types = types;
+ this.types.addAll(types);
+ this.valueClass = valueClass;
this.defaultValue = defaultValue;
}
- String getKey() {
+ public String getKey() {
return key;
}
- public List<KeyType> getTypes() {
- return types;
+ public boolean hasType(KeyType type) {
+ return types.contains(type);
+ }
+
+ public Class<T> getValueClass() {
+ return valueClass;
}
public T getDefaultValue() {
@@ -46,3 +50,48 @@ public class ConfigKey<T> {
}
}
+
+class StringConfigKey extends ConfigKey<String> {
+ StringConfigKey(String key, List<KeyType> types) {
+ super(key, types, String.class, null);
+ }
+ StringConfigKey(String key, List<KeyType> types, String defaultValue) {
+ super(key, types, String.class, defaultValue);
+ }
+}
+
+class BooleanConfigKey extends ConfigKey<Boolean> {
+ BooleanConfigKey(String key, List<KeyType> types) {
+ super(key, types, Boolean.class, null);
+ }
+ BooleanConfigKey(String key, List<KeyType> types, Boolean defaultValue) {
+ super(key, types, Boolean.class, defaultValue);
+ }
+}
+
+class IntegerConfigKey extends ConfigKey<Integer> {
+ IntegerConfigKey(String key, List<KeyType> types) {
+ super(key, types, Integer.class, null);
+ }
+ IntegerConfigKey(String key, List<KeyType> types, Integer defaultValue) {
+ super(key, types, Integer.class, defaultValue);
+ }
+}
+
+class LongConfigKey extends ConfigKey<Long> {
+ LongConfigKey(String key, List<KeyType> types) {
+ super(key, types, Long.class, null);
+ }
+ LongConfigKey(String key, List<KeyType> types, Long defaultValue) {
+ super(key, types, Long.class, defaultValue);
+ }
+}
+
+class DoubleConfigKey extends ConfigKey<Double> {
+ DoubleConfigKey(String key, List<KeyType> types) {
+ super(key, types, Double.class, null);
+ }
+ DoubleConfigKey(String key, List<KeyType> types, Double defaultValue) {
+ super(key, types, Double.class, defaultValue);
+ }
+}
diff --git a/src/main/java/org/traccar/config/ConfigSuffix.java b/src/main/java/org/traccar/config/ConfigSuffix.java
index ede4c107d..aac3219c6 100644
--- a/src/main/java/org/traccar/config/ConfigSuffix.java
+++ b/src/main/java/org/traccar/config/ConfigSuffix.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,15 +17,11 @@ package org.traccar.config;
import java.util.List;
-public class ConfigSuffix<T> {
+public abstract class ConfigSuffix<T> {
- private final String keySuffix;
- private final List<KeyType> types;
- private final T defaultValue;
-
- ConfigSuffix(String keySuffix, List<KeyType> types) {
- this(keySuffix, types, null);
- }
+ protected final String keySuffix;
+ protected final List<KeyType> types;
+ protected final T defaultValue;
ConfigSuffix(String keySuffix, List<KeyType> types, T defaultValue) {
this.keySuffix = keySuffix;
@@ -33,8 +29,71 @@ public class ConfigSuffix<T> {
this.defaultValue = defaultValue;
}
- public ConfigKey<T> withPrefix(String prefix) {
- return new ConfigKey<>(prefix + keySuffix, types, defaultValue);
+ public abstract ConfigKey<T> withPrefix(String prefix);
+
+}
+
+class StringConfigSuffix extends ConfigSuffix<String> {
+ StringConfigSuffix(String key, List<KeyType> types) {
+ super(key, types, null);
+ }
+ StringConfigSuffix(String key, List<KeyType> types, String defaultValue) {
+ super(key, types, defaultValue);
+ }
+ @Override
+ public ConfigKey<String> withPrefix(String prefix) {
+ return new StringConfigKey(prefix + keySuffix, types, defaultValue);
+ }
+}
+
+class BooleanConfigSuffix extends ConfigSuffix<Boolean> {
+ BooleanConfigSuffix(String key, List<KeyType> types) {
+ super(key, types, null);
+ }
+ BooleanConfigSuffix(String key, List<KeyType> types, Boolean defaultValue) {
+ super(key, types, defaultValue);
+ }
+ @Override
+ public ConfigKey<Boolean> withPrefix(String prefix) {
+ return new BooleanConfigKey(prefix + keySuffix, types, defaultValue);
+ }
+}
+
+class IntegerConfigSuffix extends ConfigSuffix<Integer> {
+ IntegerConfigSuffix(String key, List<KeyType> types) {
+ super(key, types, null);
+ }
+ IntegerConfigSuffix(String key, List<KeyType> types, Integer defaultValue) {
+ super(key, types, defaultValue);
+ }
+ @Override
+ public ConfigKey<Integer> withPrefix(String prefix) {
+ return new IntegerConfigKey(prefix + keySuffix, types, defaultValue);
}
+}
+class LongConfigSuffix extends ConfigSuffix<Long> {
+ LongConfigSuffix(String key, List<KeyType> types) {
+ super(key, types, null);
+ }
+ LongConfigSuffix(String key, List<KeyType> types, Long defaultValue) {
+ super(key, types, defaultValue);
+ }
+ @Override
+ public ConfigKey<Long> withPrefix(String prefix) {
+ return new LongConfigKey(prefix + keySuffix, types, defaultValue);
+ }
+}
+
+class DoubleConfigSuffix extends ConfigSuffix<Double> {
+ DoubleConfigSuffix(String key, List<KeyType> types) {
+ super(key, types, null);
+ }
+ DoubleConfigSuffix(String key, List<KeyType> types, Double defaultValue) {
+ super(key, types, defaultValue);
+ }
+ @Override
+ public ConfigKey<Double> withPrefix(String prefix) {
+ return new DoubleConfigKey(prefix + keySuffix, types, defaultValue);
+ }
}
diff --git a/src/main/java/org/traccar/config/KeyType.java b/src/main/java/org/traccar/config/KeyType.java
index 57a95c9ec..46628f9fc 100644
--- a/src/main/java/org/traccar/config/KeyType.java
+++ b/src/main/java/org/traccar/config/KeyType.java
@@ -16,7 +16,7 @@
package org.traccar.config;
public enum KeyType {
- GLOBAL,
+ CONFIG,
SERVER,
USER,
DEVICE,
diff --git a/src/main/java/org/traccar/config/Keys.java b/src/main/java/org/traccar/config/Keys.java
index 1411e8a13..09662fc85 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 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,7 +15,7 @@
*/
package org.traccar.config;
-import java.util.Collections;
+import java.util.List;
public final class Keys {
@@ -25,17 +25,39 @@ public final class Keys {
/**
* Network interface for a the protocol. If not specified, server will bind all interfaces.
*/
- public static final ConfigSuffix<String> PROTOCOL_ADDRESS = new ConfigSuffix<>(
+ public static final ConfigSuffix<String> PROTOCOL_ADDRESS = new StringConfigSuffix(
".address",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Port number for the protocol. Most protocols use TCP on the transport layer. Some protocols use UDP. Some
* support both TCP and UDP.
*/
- public static final ConfigSuffix<Integer> PROTOCOL_PORT = new ConfigSuffix<>(
+ public static final ConfigSuffix<Integer> PROTOCOL_PORT = new IntegerConfigSuffix(
".port",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
+
+ /**
+ * List of devices for polling protocols. List should contain unique ids separated by commas. Used only for polling
+ * protocols.
+ */
+ public static final ConfigSuffix<String> PROTOCOL_DEVICES = new StringConfigSuffix(
+ ".devices",
+ List.of(KeyType.CONFIG));
+
+ /**
+ * Polling interval in seconds. Used only for polling protocols.
+ */
+ public static final ConfigSuffix<Long> PROTOCOL_INTERVAL = new LongConfigSuffix(
+ ".interval",
+ List.of(KeyType.CONFIG));
+
+ /**
+ * Enable SSL support for the protocol. Not all protocols support this.
+ */
+ public static final ConfigSuffix<Boolean> PROTOCOL_SSL = new BooleanConfigSuffix(
+ ".ssl",
+ List.of(KeyType.CONFIG));
/**
* Connection timeout value in seconds. Because sometimes there is no way to detect lost TCP connection old
@@ -43,567 +65,676 @@ public final class Keys {
* problems with establishing new connections when number of devices is high or devices data connections are
* unstable.
*/
- public static final ConfigSuffix<Integer> PROTOCOL_TIMEOUT = new ConfigSuffix<>(
+ public static final ConfigSuffix<Integer> PROTOCOL_TIMEOUT = new IntegerConfigSuffix(
".timeout",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
+
+ /**
+ * Device password. Commonly used in some protocol for sending commands.
+ */
+ public static final ConfigKey<String> DEVICE_PASSWORD = new StringConfigKey(
+ "devicePassword",
+ List.of(KeyType.DEVICE));
/**
* Device password. Commonly used in some protocol for sending commands.
*/
- public static final ConfigSuffix<String> PROTOCOL_DEVICE_PASSWORD = new ConfigSuffix<>(
+ public static final ConfigSuffix<String> PROTOCOL_DEVICE_PASSWORD = new StringConfigSuffix(
".devicePassword",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Default protocol mask to use. Currently used only by Skypatrol protocol.
*/
- public static final ConfigSuffix<Integer> PROTOCOL_MASK = new ConfigSuffix<>(
+ public static final ConfigSuffix<Integer> PROTOCOL_MASK = new IntegerConfigSuffix(
".mask",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Custom message length. Currently used only by H2 protocol for specifying binary message length.
*/
- public static final ConfigSuffix<Integer> PROTOCOL_MESSAGE_LENGTH = new ConfigSuffix<>(
+ public static final ConfigSuffix<Integer> PROTOCOL_MESSAGE_LENGTH = new IntegerConfigSuffix(
".messageLength",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Enable extended functionality for the protocol. The reason it's disabled by default is that not all devices
* support it.
*/
- public static final ConfigSuffix<Boolean> PROTOCOL_EXTENDED = new ConfigSuffix<>(
+ public static final ConfigSuffix<Boolean> PROTOCOL_EXTENDED = new BooleanConfigSuffix(
".extended",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Decode string as UTF8 instead of ASCII. Only applicable for some protocols.
*/
- public static final ConfigSuffix<Boolean> PROTOCOL_UTF8 = new ConfigSuffix<>(
+ public static final ConfigSuffix<Boolean> PROTOCOL_UTF8 = new BooleanConfigSuffix(
".utf8",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Enable CAN decoding for the protocol. Similar to 'extended' configuration, it's not supported for some devices.
*/
- public static final ConfigSuffix<Boolean> PROTOCOL_CAN = new ConfigSuffix<>(
+ public static final ConfigSuffix<Boolean> PROTOCOL_CAN = new BooleanConfigSuffix(
".can",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Indicates whether server acknowledgement is required. Only applicable for some protocols.
*/
- public static final ConfigSuffix<Boolean> PROTOCOL_ACK = new ConfigSuffix<>(
+ public static final ConfigSuffix<Boolean> PROTOCOL_ACK = new BooleanConfigSuffix(
".ack",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG, KeyType.DEVICE),
+ false);
/**
* Ignore device reported fix time. Useful in case some devices report invalid time. Currently only available for
* GL200 protocol.
*/
- public static final ConfigSuffix<Boolean> PROTOCOL_IGNORE_FIX_TIME = new ConfigSuffix<>(
+ public static final ConfigSuffix<Boolean> PROTOCOL_IGNORE_FIX_TIME = new BooleanConfigSuffix(
".ignoreFixTime",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Decode additional TK103 attributes. Not supported for some devices.
*/
- public static final ConfigSuffix<Boolean> PROTOCOL_DECODE_LOW = new ConfigSuffix<>(
+ public static final ConfigSuffix<Boolean> PROTOCOL_DECODE_LOW = new BooleanConfigSuffix(
".decodeLow",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Use long date format for Atrack protocol.
*/
- public static final ConfigSuffix<Boolean> PROTOCOL_LONG_DATE = new ConfigSuffix<>(
+ public static final ConfigSuffix<Boolean> PROTOCOL_LONG_DATE = new BooleanConfigSuffix(
".longDate",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Use decimal fuel value format for Atrack protocol.
*/
- public static final ConfigSuffix<Boolean> PROTOCOL_DECIMAL_FUEL = new ConfigSuffix<>(
+ public static final ConfigSuffix<Boolean> PROTOCOL_DECIMAL_FUEL = new BooleanConfigSuffix(
".decimalFuel",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Indicates additional custom attributes for Atrack protocol.
*/
- public static final ConfigSuffix<Boolean> PROTOCOL_CUSTOM = new ConfigSuffix<>(
+ public static final ConfigSuffix<Boolean> PROTOCOL_CUSTOM = new BooleanConfigSuffix(
".custom",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Custom format string for Atrack protocol.
*/
- public static final ConfigSuffix<String> PROTOCOL_FORM = new ConfigSuffix<>(
+ public static final ConfigSuffix<String> PROTOCOL_FORM = new StringConfigSuffix(
".form",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Protocol configuration. Required for some devices for decoding incoming data.
*/
- public static final ConfigSuffix<String> PROTOCOL_CONFIG = new ConfigSuffix<>(
+ public static final ConfigSuffix<String> PROTOCOL_CONFIG = new StringConfigSuffix(
".config",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Alarm mapping for Atrack protocol.
*/
- public static final ConfigSuffix<String> PROTOCOL_ALARM_MAP = new ConfigSuffix<>(
+ public static final ConfigSuffix<String> PROTOCOL_ALARM_MAP = new StringConfigSuffix(
".alarmMap",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Indicates whether TAIP protocol should have prefixes for messages.
*/
- public static final ConfigSuffix<Boolean> PROTOCOL_PREFIX = new ConfigSuffix<>(
+ public static final ConfigSuffix<Boolean> PROTOCOL_PREFIX = new BooleanConfigSuffix(
".prefix",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Some devices require server address confirmation. Use this parameter to configure correct public address.
*/
- public static final ConfigSuffix<String> PROTOCOL_SERVER = new ConfigSuffix<>(
+ public static final ConfigSuffix<String> PROTOCOL_SERVER = new StringConfigSuffix(
".server",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
+
+ /**
+ * Protocol type for Suntech.
+ */
+ public static final ConfigKey<Integer> PROTOCOL_TYPE = new IntegerConfigKey(
+ "suntech.protocolType",
+ List.of(KeyType.CONFIG, KeyType.DEVICE));
+
+ /**
+ * Suntech HBM configuration value.
+ */
+ public static final ConfigKey<Boolean> PROTOCOL_HBM = new BooleanConfigKey(
+ "suntech.hbm",
+ List.of(KeyType.CONFIG, KeyType.DEVICE));
+
+ /**
+ * Format includes ADC value.
+ */
+ public static final ConfigSuffix<Boolean> PROTOCOL_INCLUDE_ADC = new BooleanConfigSuffix(
+ ".includeAdc",
+ List.of(KeyType.CONFIG, KeyType.DEVICE));
/**
- * Skip device connection session cache. Per protocol configuration.
+ * Format includes RPM value.
*/
- public static final ConfigSuffix<Boolean> PROTOCOL_IGNORE_SESSIONS_CACHE = new ConfigSuffix<>(
- ".ignoreSessionCache",
- Collections.singletonList(KeyType.GLOBAL));
+ public static final ConfigSuffix<Boolean> PROTOCOL_INCLUDE_RPM = new BooleanConfigSuffix(
+ ".includeRpm",
+ List.of(KeyType.CONFIG, KeyType.DEVICE));
/**
- * Skip device connection session cache. Global configuration.
+ * Format includes temperature values.
*/
- public static final ConfigKey<Boolean> DECODER_IGNORE_SESSIONS_CACHE = new ConfigKey<>(
- "decoder.ignoreSessionCache",
- Collections.singletonList(KeyType.GLOBAL));
+ public static final ConfigSuffix<Boolean> PROTOCOL_INCLUDE_TEMPERATURE = new BooleanConfigSuffix(
+ ".includeTemp",
+ List.of(KeyType.CONFIG, KeyType.DEVICE));
+
+ /**
+ * Protocol format. Used by protocols that have configurable message format.
+ */
+ public static final ConfigSuffix<String> PROTOCOL_FORMAT = new StringConfigSuffix(
+ ".format",
+ List.of(KeyType.DEVICE));
+
+ /**
+ * Protocol date format. Used by protocols that have configurable date format.
+ */
+ public static final ConfigSuffix<String> PROTOCOL_DATE_FORMAT = new StringConfigSuffix(
+ ".dateFormat",
+ List.of(KeyType.DEVICE));
+
+ /**
+ * Device time zone. Most devices report UTC time, but in some cases devices report local time, so this parameter
+ * needs to be configured for the server to be able to decode the time correctly.
+ */
+ public static final ConfigKey<String> DECODER_TIMEZONE = new StringConfigKey(
+ "decoder.timezone",
+ List.of(KeyType.CONFIG, KeyType.DEVICE));
+
+ /**
+ * ORBCOMM API access id.
+ */
+ public static final ConfigKey<String> ORBCOMM_ACCESS_ID = new StringConfigKey(
+ "orbcomm.accessId",
+ List.of(KeyType.CONFIG));
+
+ /**
+ * ORBCOMM API password.
+ */
+ public static final ConfigKey<String> ORBCOMM_PASSWORD = new StringConfigKey(
+ "orbcomm.password",
+ List.of(KeyType.CONFIG));
+
+ /**
+ * Use alternative format for the protocol of commands.
+ */
+ public static final ConfigSuffix<Boolean> PROTOCOL_ALTERNATIVE = new BooleanConfigSuffix(
+ ".alternative",
+ List.of(KeyType.CONFIG, KeyType.DEVICE),
+ false);
+
+ /**
+ * Protocol format includes a language field.
+ */
+ public static final ConfigSuffix<Boolean> PROTOCOL_LANGUAGE = new BooleanConfigSuffix(
+ ".language",
+ List.of(KeyType.CONFIG, KeyType.DEVICE),
+ false);
/**
* Server wide connection timeout value in seconds. See protocol timeout for more information.
*/
- public static final ConfigKey<Integer> SERVER_TIMEOUT = new ConfigKey<>(
+ public static final ConfigKey<Integer> SERVER_TIMEOUT = new IntegerConfigKey(
"server.timeout",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Address for uploading aggregated anonymous usage statistics. Uploaded information is the same you can see on the
* statistics screen in the web app. It does not include any sensitive (e.g. locations).
*/
- public static final ConfigKey<String> SERVER_STATISTICS = new ConfigKey<>(
+ public static final ConfigKey<String> SERVER_STATISTICS = new StringConfigKey(
"server.statistics",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
- * If true, the event is generated once at the beginning of overspeeding period.
+ * Fuel drop threshold value. When fuel level drops from one position to another for more the value, an event is
+ * generated.
*/
- public static final ConfigKey<Boolean> EVENT_OVERSPEED_NOT_REPEAT = new ConfigKey<>(
- "event.overspeed.notRepeat",
- Collections.singletonList(KeyType.GLOBAL));
+ public static final ConfigKey<Double> EVENT_FUEL_DROP_THRESHOLD = new DoubleConfigKey(
+ "fuelDropThreshold",
+ List.of(KeyType.SERVER, KeyType.DEVICE),
+ 0.0);
+
+ /**
+ * Fuel increase threshold value. When fuel level increases from one position to another for more the value, an
+ * event is generated.
+ */
+ public static final ConfigKey<Double> EVENT_FUEL_INCREASE_THRESHOLD = new DoubleConfigKey(
+ "fuelIncreaseThreshold",
+ List.of(KeyType.SERVER, KeyType.DEVICE),
+ 0.0);
+
+ /**
+ * Speed limit value in knots.
+ */
+ public static final ConfigKey<Double> EVENT_OVERSPEED_LIMIT = new DoubleConfigKey(
+ "speedLimit",
+ List.of(KeyType.SERVER, KeyType.DEVICE),
+ 0.0);
/**
* Minimal over speed duration to trigger the event. Value in seconds.
*/
- public static final ConfigKey<Long> EVENT_OVERSPEED_MINIMAL_DURATION = new ConfigKey<>(
+ public static final ConfigKey<Long> EVENT_OVERSPEED_MINIMAL_DURATION = new LongConfigKey(
"event.overspeed.minimalDuration",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Relevant only for geofence speed limits. Use the lowest speed limit from all geofences.
*/
- public static final ConfigKey<Boolean> EVENT_OVERSPEED_PREFER_LOWEST = new ConfigKey<>(
+ public static final ConfigKey<Boolean> EVENT_OVERSPEED_PREFER_LOWEST = new BooleanConfigKey(
"event.overspeed.preferLowest",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Driver behavior acceleration threshold. Value is in meter per second squared.
*/
- public static final ConfigKey<Double> EVENT_BEHAVIOR_ACCELERATION_THRESHOLD = new ConfigKey<>(
+ public static final ConfigKey<Double> EVENT_BEHAVIOR_ACCELERATION_THRESHOLD = new DoubleConfigKey(
"event.behavior.accelerationThreshold",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Driver behavior braking threshold. Value is in meter per second squared.
*/
- public static final ConfigKey<Double> EVENT_BEHAVIOR_BRAKING_THRESHOLD = new ConfigKey<>(
+ public static final ConfigKey<Double> EVENT_BEHAVIOR_BRAKING_THRESHOLD = new DoubleConfigKey(
"event.behavior.brakingThreshold",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Do not generate alert event if same alert was present in last known location.
*/
- public static final ConfigKey<Boolean> EVENT_IGNORE_DUPLICATE_ALERTS = new ConfigKey<>(
+ public static final ConfigKey<Boolean> EVENT_IGNORE_DUPLICATE_ALERTS = new BooleanConfigKey(
"event.ignoreDuplicateAlerts",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* If set to true, invalid positions will be considered for motion logic.
*/
- public static final ConfigKey<Boolean> EVENT_MOTION_PROCESS_INVALID_POSITIONS = new ConfigKey<>(
+ public static final ConfigKey<Boolean> EVENT_MOTION_PROCESS_INVALID_POSITIONS = new BooleanConfigKey(
"event.motion.processInvalidPositions",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* If the speed is above specified value, the object is considered to be in motion. Default value is 0.01 knots.
*/
- public static final ConfigKey<Double> EVENT_MOTION_SPEED_THRESHOLD = new ConfigKey<>(
+ public static final ConfigKey<Double> EVENT_MOTION_SPEED_THRESHOLD = new DoubleConfigKey(
"event.motion.speedThreshold",
- Collections.singletonList(KeyType.GLOBAL),
+ List.of(KeyType.CONFIG),
0.01);
/**
* Global polyline geofence distance. Within that distance from the polyline, point is considered within the
* geofence. Each individual geofence can also has 'polylineDistance' attribute which will take precedence.
*/
- public static final ConfigKey<Double> GEOFENCE_POLYLINE_DISTANCE = new ConfigKey<>(
+ public static final ConfigKey<Double> GEOFENCE_POLYLINE_DISTANCE = new DoubleConfigKey(
"geofence.polylineDistance",
- Collections.singletonList(KeyType.GLOBAL),
+ List.of(KeyType.CONFIG),
25.0);
/**
* Path to the database driver JAR file. Traccar includes drivers for MySQL, PostgreSQL and H2 databases. If you use
* one of those, you don't need to specify this parameter.
*/
- public static final ConfigKey<String> DATABASE_DRIVER_FILE = new ConfigKey<>(
+ public static final ConfigKey<String> DATABASE_DRIVER_FILE = new StringConfigKey(
"database.driverFile",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Database driver Java class. For H2 use 'org.h2.Driver'. MySQL driver class name is 'com.mysql.jdbc.Driver'.
*/
- public static final ConfigKey<String> DATABASE_DRIVER = new ConfigKey<>(
+ public static final ConfigKey<String> DATABASE_DRIVER = new StringConfigKey(
"database.driver",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Database connection URL. By default Traccar uses H2 database.
*/
- public static final ConfigKey<String> DATABASE_URL = new ConfigKey<>(
+ public static final ConfigKey<String> DATABASE_URL = new StringConfigKey(
"database.url",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Database user name. Default administrator user for H2 database is 'sa'.
*/
- public static final ConfigKey<String> DATABASE_USER = new ConfigKey<>(
+ public static final ConfigKey<String> DATABASE_USER = new StringConfigKey(
"database.user",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Database user password. Default password for H2 admin (sa) user is empty.
*/
- public static final ConfigKey<String> DATABASE_PASSWORD = new ConfigKey<>(
+ public static final ConfigKey<String> DATABASE_PASSWORD = new StringConfigKey(
"database.password",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Path to Liquibase master changelog file.
*/
- public static final ConfigKey<String> DATABASE_CHANGELOG = new ConfigKey<>(
+ public static final ConfigKey<String> DATABASE_CHANGELOG = new StringConfigKey(
"database.changelog",
- Collections.singletonList(KeyType.GLOBAL));
-
- /**
- * Automatically generate SQL database queries when possible.
- */
- public static final ConfigKey<Boolean> DATABASE_GENERATE_QUERIES = new ConfigKey<>(
- "database.generateQueries",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Database connection pool size. Default value is defined by the HikariCP library.
*/
- public static final ConfigKey<Integer> DATABASE_MAX_POOL_SIZE = new ConfigKey<>(
+ public static final ConfigKey<Integer> DATABASE_MAX_POOL_SIZE = new IntegerConfigKey(
"database.maxPoolSize",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* SQL query to check connection status. Default value is 'SELECT 1'. For Oracle database you can use
* 'SELECT 1 FROM DUAL'.
*/
- public static final ConfigKey<String> DATABASE_CHECK_CONNECTION = new ConfigKey<>(
+ public static final ConfigKey<String> DATABASE_CHECK_CONNECTION = new StringConfigKey(
"database.checkConnection",
- Collections.singletonList(KeyType.GLOBAL),
+ List.of(KeyType.CONFIG),
"SELECT 1");
/**
* Store original HEX or string data as "raw" attribute in the corresponding position.
*/
- public static final ConfigKey<Boolean> DATABASE_SAVE_ORIGINAL = new ConfigKey<>(
+ public static final ConfigKey<Boolean> DATABASE_SAVE_ORIGINAL = new BooleanConfigKey(
"database.saveOriginal",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
+
+ /**
+ * Throttle unknown device database queries when it sends repeated requests.
+ */
+ public static final ConfigKey<Boolean> DATABASE_THROTTLE_UNKNOWN = new BooleanConfigKey(
+ "database.throttleUnknown",
+ List.of(KeyType.CONFIG));
/**
- * By default server syncs with the database if it encounters and unknown device. This flag allows to disable that
+ * By default, server syncs with the database if it encounters and unknown device. This flag allows to disable that
* behavior to improve performance in some cases.
*/
- public static final ConfigKey<Boolean> DATABASE_IGNORE_UNKNOWN = new ConfigKey<>(
+ public static final ConfigKey<Boolean> DATABASE_IGNORE_UNKNOWN = new BooleanConfigKey(
"database.ignoreUnknown",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Automatically register unknown devices in the database.
*/
- public static final ConfigKey<Boolean> DATABASE_REGISTER_UNKNOWN = new ConfigKey<>(
+ public static final ConfigKey<Boolean> DATABASE_REGISTER_UNKNOWN = new BooleanConfigKey(
"database.registerUnknown",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Default category for auto-registered devices.
*/
- public static final ConfigKey<String> DATABASE_REGISTER_UNKNOWN_DEFAULT_CATEGORY = new ConfigKey<>(
+ public static final ConfigKey<String> DATABASE_REGISTER_UNKNOWN_DEFAULT_CATEGORY = new StringConfigKey(
"database.registerUnknown.defaultCategory",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* The group id assigned to auto-registered devices.
*/
- public static final ConfigKey<Long> DATABASE_REGISTER_UNKNOWN_DEFAULT_GROUP_ID = new ConfigKey<>(
+ public static final ConfigKey<Long> DATABASE_REGISTER_UNKNOWN_DEFAULT_GROUP_ID = new LongConfigKey(
"database.registerUnknown.defaultGroupId",
- Collections.singletonList(KeyType.GLOBAL));
-
- /**
- * Minimum device refresh timeout in seconds. Default timeout is 5 minutes.
- */
- public static final ConfigKey<Long> DATABASE_REFRESH_DELAY = new ConfigKey<>(
- "database.refreshDelay",
- Collections.singletonList(KeyType.GLOBAL),
- 300L);
+ List.of(KeyType.CONFIG));
/**
* Store empty messages as positions. For example, heartbeats.
*/
- public static final ConfigKey<Boolean> DATABASE_SAVE_EMPTY = new ConfigKey<>(
+ public static final ConfigKey<Boolean> DATABASE_SAVE_EMPTY = new BooleanConfigKey(
"database.saveEmpty",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Device limit for self registered users. Default value is -1, which indicates no limit.
*/
- public static final ConfigKey<Integer> USERS_DEFAULT_DEVICE_LIMIT = new ConfigKey<>(
+ public static final ConfigKey<Integer> USERS_DEFAULT_DEVICE_LIMIT = new IntegerConfigKey(
"users.defaultDeviceLimit",
- Collections.singletonList(KeyType.GLOBAL),
+ List.of(KeyType.CONFIG),
-1);
/**
* Default user expiration for self registered users. Value is in days. By default no expiration is set.
*/
- public static final ConfigKey<Integer> USERS_DEFAULT_EXPIRATION_DAYS = new ConfigKey<>(
+ public static final ConfigKey<Integer> USERS_DEFAULT_EXPIRATION_DAYS = new IntegerConfigKey(
"users.defaultExpirationDays",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* LDAP server URL.
*/
- public static final ConfigKey<String> LDAP_URL = new ConfigKey<>(
+ public static final ConfigKey<String> LDAP_URL = new StringConfigKey(
"ldap.url",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* LDAP server login.
*/
- public static final ConfigKey<String> LDAP_USER = new ConfigKey<>(
+ public static final ConfigKey<String> LDAP_USER = new StringConfigKey(
"ldap.user",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* LDAP server password.
*/
- public static final ConfigKey<String> LDAP_PASSWORD = new ConfigKey<>(
+ public static final ConfigKey<String> LDAP_PASSWORD = new StringConfigKey(
"ldap.password",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Force LDAP authentication.
*/
- public static final ConfigKey<Boolean> LDAP_FORCE = new ConfigKey<>(
+ public static final ConfigKey<Boolean> LDAP_FORCE = new BooleanConfigKey(
"ldap.force",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* LDAP user search base.
*/
- public static final ConfigKey<String> LDAP_BASE = new ConfigKey<>(
+ public static final ConfigKey<String> LDAP_BASE = new StringConfigKey(
"ldap.base",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* LDAP attribute used as user id. Default value is 'uid'.
*/
- public static final ConfigKey<String> LDAP_ID_ATTRIBUTE = new ConfigKey<>(
+ public static final ConfigKey<String> LDAP_ID_ATTRIBUTE = new StringConfigKey(
"ldap.idAttribute",
- Collections.singletonList(KeyType.GLOBAL),
+ List.of(KeyType.CONFIG),
"uid");
/**
* LDAP attribute used as user name. Default value is 'cn'.
*/
- public static final ConfigKey<String> LDAP_NAME_ATTRIBUTE = new ConfigKey<>(
+ public static final ConfigKey<String> LDAP_NAME_ATTRIBUTE = new StringConfigKey(
"ldap.nameAttribute",
- Collections.singletonList(KeyType.GLOBAL),
+ List.of(KeyType.CONFIG),
"cn");
/**
* LDAP attribute used as user email. Default value is 'mail'.
*/
- public static final ConfigKey<String> LDAP_MAIN_ATTRIBUTE = new ConfigKey<>(
+ public static final ConfigKey<String> LDAP_MAIN_ATTRIBUTE = new StringConfigKey(
"ldap.mailAttribute",
- Collections.singletonList(KeyType.GLOBAL),
+ List.of(KeyType.CONFIG),
"mail");
/**
* LDAP custom search filter. If not specified, '({idAttribute}=:login)' will be used as a filter.
*/
- public static final ConfigKey<String> LDAP_SEARCH_FILTER = new ConfigKey<>(
+ public static final ConfigKey<String> LDAP_SEARCH_FILTER = new StringConfigKey(
"ldap.searchFilter",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* LDAP custom admin search filter.
*/
- public static final ConfigKey<String> LDAP_ADMIN_FILTER = new ConfigKey<>(
+ public static final ConfigKey<String> LDAP_ADMIN_FILTER = new StringConfigKey(
"ldap.adminFilter",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* LDAP admin user group. Used if custom admin filter is not specified.
*/
- public static final ConfigKey<String> LDAP_ADMIN_GROUP = new ConfigKey<>(
+ public static final ConfigKey<String> LDAP_ADMIN_GROUP = new StringConfigKey(
"ldap.adminGroup",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* If no data is reported by a device for the given amount of time, status changes from online to unknown. Value is
* in seconds. Default timeout is 10 minutes.
*/
- public static final ConfigKey<Long> STATUS_TIMEOUT = new ConfigKey<>(
+ public static final ConfigKey<Long> STATUS_TIMEOUT = new LongConfigKey(
"status.timeout",
- Collections.singletonList(KeyType.GLOBAL),
+ List.of(KeyType.CONFIG),
600L);
/**
- * Force additional state check when device status changes to 'offline' or 'unknown'. Default false.
- */
- public static final ConfigKey<Boolean> STATUS_UPDATE_DEVICE_STATE = new ConfigKey<>(
- "status.updateDeviceState",
- Collections.singletonList(KeyType.GLOBAL));
-
- /**
* List of protocol names to ignore offline status. Can be useful to not trigger status change when devices are
* configured to disconnect after reporting a batch of data.
*/
- public static final ConfigKey<String> STATUS_IGNORE_OFFLINE = new ConfigKey<>(
+ public static final ConfigKey<String> STATUS_IGNORE_OFFLINE = new StringConfigKey(
"status.ignoreOffline",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Path to the media folder. Server stores audio, video and photo files in that folder. Sub-folders will be
* automatically created for each device by unique id.
*/
- public static final ConfigKey<String> MEDIA_PATH = new ConfigKey<>(
+ public static final ConfigKey<String> MEDIA_PATH = new StringConfigKey(
"media.path",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Optional parameter to specify network interface for web interface to bind to. By default server will bind to all
* available interfaces.
*/
- public static final ConfigKey<String> WEB_ADDRESS = new ConfigKey<>(
+ public static final ConfigKey<String> WEB_ADDRESS = new StringConfigKey(
"web.address",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Web interface TCP port number. By default Traccar uses port 8082. To avoid specifying port in the browser you
* can set it to 80 (default HTTP port).
*/
- public static final ConfigKey<Integer> WEB_PORT = new ConfigKey<>(
+ public static final ConfigKey<Integer> WEB_PORT = new IntegerConfigKey(
"web.port",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
+
+ /**
+ * Maximum API requests per second. Above this limit requests and delayed and throttled.
+ */
+ public static final ConfigKey<Integer> WEB_MAX_REQUESTS_PER_SECOND = new IntegerConfigKey(
+ "web.maxRequestsPerSec",
+ List.of(KeyType.CONFIG));
+
+ /**
+ * Sanitize all strings returned via API. This is needed to fix XSS issues in the old web interface. New React-based
+ * interface doesn't require this.
+ */
+ public static final ConfigKey<Boolean> WEB_SANITIZE = new BooleanConfigKey(
+ "web.sanitize",
+ List.of(KeyType.CONFIG));
/**
* Path to the web app folder.
*/
- public static final ConfigKey<String> WEB_PATH = new ConfigKey<>(
+ public static final ConfigKey<String> WEB_PATH = new StringConfigKey(
"web.path",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* WebSocket connection timeout in milliseconds. Default timeout is 10 minutes.
*/
- public static final ConfigKey<Long> WEB_TIMEOUT = new ConfigKey<>(
+ public static final ConfigKey<Long> WEB_TIMEOUT = new LongConfigKey(
"web.timeout",
- Collections.singletonList(KeyType.GLOBAL),
+ List.of(KeyType.CONFIG),
60000L);
/**
* Authentication sessions timeout in seconds. By default no timeout.
*/
- public static final ConfigKey<Integer> WEB_SESSION_TIMEOUT = new ConfigKey<>(
+ public static final ConfigKey<Integer> WEB_SESSION_TIMEOUT = new IntegerConfigKey(
"web.sessionTimeout",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Enable database access console via '/console' URL. Use only for debugging. Never use in production.
*/
- public static final ConfigKey<Boolean> WEB_CONSOLE = new ConfigKey<>(
+ public static final ConfigKey<Boolean> WEB_CONSOLE = new BooleanConfigKey(
"web.console",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Server debug version of the web app. Not recommended to use for performance reasons. It is intended to be used
* for development and debugging purposes.
*/
- public static final ConfigKey<Boolean> WEB_DEBUG = new ConfigKey<>(
+ public static final ConfigKey<Boolean> WEB_DEBUG = new BooleanConfigKey(
"web.debug",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
+
+ /**
+ * A token to login as a virtual admin account. Can be used to restore access in case of issues with regular admin
+ * login. For example, if password is lost and can't be restored.
+ */
+ public static final ConfigKey<String> WEB_SERVICE_ACCOUNT_TOKEN = new StringConfigKey(
+ "web.serviceAccountToken",
+ List.of(KeyType.CONFIG));
/**
* Cross-origin resource sharing origin header value.
*/
- public static final ConfigKey<String> WEB_ORIGIN = new ConfigKey<>(
+ public static final ConfigKey<String> WEB_ORIGIN = new StringConfigKey(
"web.origin",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Cache control header value. By default resources are cached for one hour.
*/
- public static final ConfigKey<String> WEB_CACHE_CONTROL = new ConfigKey<>(
+ public static final ConfigKey<String> WEB_CACHE_CONTROL = new StringConfigKey(
"web.cacheControl",
- Collections.singletonList(KeyType.GLOBAL),
+ List.of(KeyType.CONFIG),
"max-age=3600,public");
/**
- * URL to forward positions. Data is passed through URL parameters. For example, {uniqueId} for device identifier,
- * {latitude} and {longitude} for coordinates.
+ * Position forwarding format. Available options are "url", "json" and "kafka". Default is "url".
*/
- public static final ConfigKey<String> FORWARD_URL = new ConfigKey<>(
- "forward.url",
- Collections.singletonList(KeyType.GLOBAL));
+ public static final ConfigKey<String> FORWARD_TYPE = new StringConfigKey(
+ "forward.type",
+ List.of(KeyType.CONFIG),
+ "url");
/**
- * Additional HTTP header, can be used for authorization.
+ * Position forwarding Kafka topic.
*/
- public static final ConfigKey<String> FORWARD_HEADER = new ConfigKey<>(
- "forward.header",
- Collections.singletonList(KeyType.GLOBAL));
+ public static final ConfigKey<String> FORWARD_TOPIC = new StringConfigKey(
+ "forward.topic",
+ List.of(KeyType.CONFIG),
+ "positions");
/**
- * Boolean value to enable forwarding in JSON format.
+ * URL to forward positions. Data is passed through URL parameters. For example, {uniqueId} for device identifier,
+ * {latitude} and {longitude} for coordinates.
*/
- public static final ConfigKey<Boolean> FORWARD_JSON = new ConfigKey<>(
- "forward.json",
- Collections.singletonList(KeyType.GLOBAL));
+ public static final ConfigKey<String> FORWARD_URL = new StringConfigKey(
+ "forward.url",
+ List.of(KeyType.CONFIG));
/**
- * Boolean value to enable URL parameters in json mode. For example, {uniqueId} for device identifier,
- * {latitude} and {longitude} for coordinates.
+ * Additional HTTP header, can be used for authorization.
*/
- public static final ConfigKey<Boolean> FORWARD_URL_VARIABLES = new ConfigKey<>(
- "forward.urlVariables",
- Collections.singletonList(KeyType.GLOBAL));
+ public static final ConfigKey<String> FORWARD_HEADER = new StringConfigKey(
+ "forward.header",
+ List.of(KeyType.CONFIG));
/**
* Position forwarding retrying enable. When enabled, additional attempts are made to deliver positions. If initial
@@ -612,573 +743,745 @@ public final class Keys {
* If forwarding is retried for 'forward.retry.count', retrying is canceled and the position is dropped. Positions
* pending to be delivered are limited to 'forward.retry.limit'. If this limit is reached, positions get discarded.
*/
- public static final ConfigKey<Boolean> FORWARD_RETRY_ENABLE = new ConfigKey<>(
+ public static final ConfigKey<Boolean> FORWARD_RETRY_ENABLE = new BooleanConfigKey(
"forward.retry.enable",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Position forwarding retry first delay in milliseconds.
* Can be set to anything greater than 0. Defaults to 100 milliseconds.
*/
- public static final ConfigKey<Integer> FORWARD_RETRY_DELAY = new ConfigKey<>(
+ public static final ConfigKey<Integer> FORWARD_RETRY_DELAY = new IntegerConfigKey(
"forward.retry.delay",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG),
+ 100);
/**
* Position forwarding retry maximum retries.
* Can be set to anything greater than 0. Defaults to 10 retries.
*/
- public static final ConfigKey<Integer> FORWARD_RETRY_COUNT = new ConfigKey<>(
+ public static final ConfigKey<Integer> FORWARD_RETRY_COUNT = new IntegerConfigKey(
"forward.retry.count",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG),
+ 10);
/**
* Position forwarding retry pending positions limit.
* Can be set to anything greater than 0. Defaults to 100 positions.
*/
- public static final ConfigKey<Integer> FORWARD_RETRY_LIMIT = new ConfigKey<>(
+ public static final ConfigKey<Integer> FORWARD_RETRY_LIMIT = new IntegerConfigKey(
"forward.retry.limit",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG),
+ 100);
+
+ /**
+ * Events forwarding format. Available options are "json" and "kafka". Default is "json".
+ */
+ public static final ConfigKey<String> EVENT_FORWARD_TYPE = new StringConfigKey(
+ "event.forward.type",
+ List.of(KeyType.CONFIG),
+ "json");
+
+ /**
+ * Events forwarding Kafka topic.
+ */
+ public static final ConfigKey<String> EVENT_FORWARD_TOPIC = new StringConfigKey(
+ "event.forward.topic",
+ List.of(KeyType.CONFIG),
+ "events");
/**
* Events forwarding URL.
*/
- public static final ConfigKey<String> EVENT_FORWARD_URL = new ConfigKey<>(
+ public static final ConfigKey<String> EVENT_FORWARD_URL = new StringConfigKey(
"event.forward.url",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Events forwarding headers. Example value:
* FirstHeader: hello
* SecondHeader: world
*/
- public static final ConfigKey<String> EVENT_FORWARD_HEADERS = new ConfigKey<>(
+ public static final ConfigKey<String> EVENT_FORWARD_HEADERS = new StringConfigKey(
"event.forward.header",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
+
+ /**
+ * Root folder for all template files.
+ */
+ public static final ConfigKey<String> TEMPLATES_ROOT = new StringConfigKey(
+ "templates.root",
+ List.of(KeyType.CONFIG),
+ "templates");
+
+ /**
+ * Log emails instead of sending them via SMTP. Intended for testing purposes only.
+ */
+ public static final ConfigKey<Boolean> MAIL_DEBUG = new BooleanConfigKey(
+ "mail.debug",
+ List.of(KeyType.CONFIG));
+
+ /**
+ * Force SMTP settings from the config file and ignore user attributes.
+ */
+ public static final ConfigKey<Boolean> MAIL_SMTP_IGNORE_USER_CONFIG = new BooleanConfigKey(
+ "mail.smtp.ignoreUserConfig",
+ List.of(KeyType.CONFIG));
+
+ /**
+ * The SMTP server to connect to.
+ */
+ public static final ConfigKey<String> MAIL_SMTP_HOST = new StringConfigKey(
+ "mail.smtp.host",
+ List.of(KeyType.CONFIG, KeyType.USER));
+
+ /**
+ * The SMTP server port to connect. Defaults to 25.
+ */
+ public static final ConfigKey<Integer> MAIL_SMTP_PORT = new IntegerConfigKey(
+ "mail.smtp.port",
+ List.of(KeyType.CONFIG, KeyType.USER),
+ 25);
+
+ /**
+ * Email transport protocol. Default value is "smtp".
+ */
+ public static final ConfigKey<String> MAIL_TRANSPORT_PROTOCOL = new StringConfigKey(
+ "mail.transport.protocol",
+ List.of(KeyType.CONFIG, KeyType.USER),
+ "smtp");
+
+ /**
+ * If true, enables the use of the STARTTLS command (if supported by the server) to switch the connection to a
+ * TLS-protected connection before issuing any login commands.
+ */
+ public static final ConfigKey<Boolean> MAIL_SMTP_STARTTLS_ENABLE = new BooleanConfigKey(
+ "mail.smtp.starttls.enable",
+ List.of(KeyType.CONFIG, KeyType.USER));
+
+ /**
+ * If true, requires the use of the STARTTLS command. If the server doesn't support the STARTTLS command, or the
+ * command fails, the connect method will fail.
+ */
+ public static final ConfigKey<Boolean> MAIL_SMTP_STARTTLS_REQUIRED = new BooleanConfigKey(
+ "mail.smtp.starttls.required",
+ List.of(KeyType.CONFIG, KeyType.USER));
+
+ /**
+ * If set to true, use SSL to connect and use the SSL port by default.
+ */
+ public static final ConfigKey<Boolean> MAIL_SMTP_SSL_ENABLE = new BooleanConfigKey(
+ "mail.smtp.ssl.enable",
+ List.of(KeyType.CONFIG, KeyType.USER));
+
+ /**
+ * If set to "*", all hosts are trusted. If set to a whitespace separated list of hosts, those hosts are trusted.
+ * Otherwise, trust depends on the certificate the server presents.
+ */
+ public static final ConfigKey<String> MAIL_SMTP_SSL_TRUST = new StringConfigKey(
+ "mail.smtp.ssl.trust",
+ List.of(KeyType.CONFIG, KeyType.USER));
+
+ /**
+ * Specifies the SSL protocols that will be enabled for SSL connections.
+ */
+ public static final ConfigKey<String> MAIL_SMTP_SSL_PROTOCOLS = new StringConfigKey(
+ "mail.smtp.ssl.protocols",
+ List.of(KeyType.CONFIG, KeyType.USER));
+
+ /**
+ * SMTP connection username.
+ */
+ public static final ConfigKey<String> MAIL_SMTP_USERNAME = new StringConfigKey(
+ "mail.smtp.username",
+ List.of(KeyType.CONFIG, KeyType.USER));
+
+ /**
+ * SMTP connection password.
+ */
+ public static final ConfigKey<String> MAIL_SMTP_PASSWORD = new StringConfigKey(
+ "mail.smtp.password",
+ List.of(KeyType.CONFIG, KeyType.USER));
+
+ /**
+ * Email address to use for SMTP MAIL command.
+ */
+ public static final ConfigKey<String> MAIL_SMTP_FROM = new StringConfigKey(
+ "mail.smtp.from",
+ List.of(KeyType.CONFIG, KeyType.USER));
/**
- * Enable commands queuing when devices are offline. Commands are buffered in memory only, so restarting service
- * will clear the buffer.
+ * The personal name for the email from address.
*/
- public static final ConfigKey<Boolean> COMMANDS_QUEUEING = new ConfigKey<>(
- "commands.queueing",
- Collections.singletonList(KeyType.GLOBAL));
+ public static final ConfigKey<String> MAIL_SMTP_FROM_NAME = new StringConfigKey(
+ "mail.smtp.fromName",
+ List.of(KeyType.CONFIG, KeyType.USER));
/**
* SMS API service full URL. Enables SMS commands and notifications.
*/
- public static final ConfigKey<String> SMS_HTTP_URL = new ConfigKey<>(
+ public static final ConfigKey<String> SMS_HTTP_URL = new StringConfigKey(
"sms.http.url",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* SMS API authorization header name. Default value is 'Authorization'.
*/
- public static final ConfigKey<String> SMS_HTTP_AUTHORIZATION_HEADER = new ConfigKey<>(
+ public static final ConfigKey<String> SMS_HTTP_AUTHORIZATION_HEADER = new StringConfigKey(
"sms.http.authorizationHeader",
- Collections.singletonList(KeyType.GLOBAL),
+ List.of(KeyType.CONFIG),
"Authorization");
/**
* SMS API authorization header value. This value takes precedence over user and password.
*/
- public static final ConfigKey<String> SMS_HTTP_AUTHORIZATION = new ConfigKey<>(
+ public static final ConfigKey<String> SMS_HTTP_AUTHORIZATION = new StringConfigKey(
"sms.http.authorization",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* SMS API basic authentication user.
*/
- public static final ConfigKey<String> SMS_HTTP_USER = new ConfigKey<>(
+ public static final ConfigKey<String> SMS_HTTP_USER = new StringConfigKey(
"sms.http.user",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* SMS API basic authentication password.
*/
- public static final ConfigKey<String> SMS_HTTP_PASSWORD = new ConfigKey<>(
+ public static final ConfigKey<String> SMS_HTTP_PASSWORD = new StringConfigKey(
"sms.http.password",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* SMS API body template. Placeholders {phone} and {message} can be used in the template.
* If value starts with '{' or '[', server automatically assumes JSON format.
*/
- public static final ConfigKey<String> SMS_HTTP_TEMPLATE = new ConfigKey<>(
+ public static final ConfigKey<String> SMS_HTTP_TEMPLATE = new StringConfigKey(
"sms.http.template",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* AWS Access Key with SNS permission.
*/
- public static final ConfigKey<String> SMS_AWS_ACCESS = new ConfigKey<>(
+ public static final ConfigKey<String> SMS_AWS_ACCESS = new StringConfigKey(
"sms.aws.access",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* AWS Secret Access Key with SNS permission.
*/
- public static final ConfigKey<String> SMS_AWS_SECRET = new ConfigKey<>(
+ public static final ConfigKey<String> SMS_AWS_SECRET = new StringConfigKey(
"sms.aws.secret",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* AWS Region for SNS service.
* Make sure to use regions that are supported for messaging.
*/
- public static final ConfigKey<String> SMS_AWS_REGION = new ConfigKey<>(
+ public static final ConfigKey<String> SMS_AWS_REGION = new StringConfigKey(
"sms.aws.region",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
+
+ /**
+ * Enabled notification options. Comma-separated string is expected.
+ * Example: web,mail,sms
+ */
+ public static final ConfigKey<String> NOTIFICATOR_TYPES = new StringConfigKey(
+ "notificator.types",
+ List.of(KeyType.CONFIG));
/**
* Traccar notification API key.
*/
- public static final ConfigKey<String> NOTIFICATOR_TRACCAR_KEY = new ConfigKey<>(
+ public static final ConfigKey<String> NOTIFICATOR_TRACCAR_KEY = new StringConfigKey(
"notificator.traccar.key",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
- * Firebase server API key for push notifications.
+ * Firebase service account JSON.
*/
- public static final ConfigKey<String> NOTIFICATOR_FIREBASE_KEY = new ConfigKey<>(
- "notificator.firebase.key",
- Collections.singletonList(KeyType.GLOBAL));
+ public static final ConfigKey<String> NOTIFICATOR_FIREBASE_SERVICE_ACCOUNT = new StringConfigKey(
+ "notificator.firebase.serviceAccount",
+ List.of(KeyType.CONFIG));
/**
* Pushover notification user name.
*/
- public static final ConfigKey<String> NOTIFICATOR_PUSHOVER_USER = new ConfigKey<>(
+ public static final ConfigKey<String> NOTIFICATOR_PUSHOVER_USER = new StringConfigKey(
"notificator.pushover.user",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Pushover notification user token.
*/
- public static final ConfigKey<String> NOTIFICATOR_PUSHOVER_TOKEN = new ConfigKey<>(
+ public static final ConfigKey<String> NOTIFICATOR_PUSHOVER_TOKEN = new StringConfigKey(
"notificator.pushover.token",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Telegram notification API key.
*/
- public static final ConfigKey<String> NOTIFICATOR_TELEGRAM_KEY = new ConfigKey<>(
+ public static final ConfigKey<String> NOTIFICATOR_TELEGRAM_KEY = new StringConfigKey(
"notificator.telegram.key",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Telegram notification chat id to post messages to.
*/
- public static final ConfigKey<String> NOTIFICATOR_TELEGRAM_CHAT_ID = new ConfigKey<>(
+ public static final ConfigKey<String> NOTIFICATOR_TELEGRAM_CHAT_ID = new StringConfigKey(
"notificator.telegram.chatId",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Telegram notification send location message.
*/
- public static final ConfigKey<Boolean> NOTIFICATOR_TELEGRAM_SEND_LOCATION = new ConfigKey<>(
+ public static final ConfigKey<Boolean> NOTIFICATOR_TELEGRAM_SEND_LOCATION = new BooleanConfigKey(
"notificator.telegram.sendLocation",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Maximum time period for reports in seconds. Can be useful to prevent users to request unreasonably long reports.
* By default there is no limit.
*/
- public static final ConfigKey<Long> REPORT_PERIOD_LIMIT = new ConfigKey<>(
+ public static final ConfigKey<Long> REPORT_PERIOD_LIMIT = new LongConfigKey(
"report.periodLimit",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Trips less than minimal duration and minimal distance are ignored. 300 seconds and 500 meters are default.
*/
- public static final ConfigKey<Long> REPORT_TRIP_MINIMAL_TRIP_DISTANCE = new ConfigKey<>(
+ public static final ConfigKey<Long> REPORT_TRIP_MINIMAL_TRIP_DISTANCE = new LongConfigKey(
"report.trip.minimalTripDistance",
- Collections.singletonList(KeyType.GLOBAL),
+ List.of(KeyType.CONFIG),
500L);
/**
* Trips less than minimal duration and minimal distance are ignored. 300 seconds and 500 meters are default.
*/
- public static final ConfigKey<Long> REPORT_TRIP_MINIMAL_TRIP_DURATION = new ConfigKey<>(
+ public static final ConfigKey<Long> REPORT_TRIP_MINIMAL_TRIP_DURATION = new LongConfigKey(
"report.trip.minimalTripDuration",
- Collections.singletonList(KeyType.GLOBAL),
+ List.of(KeyType.CONFIG),
300L);
/**
* Parking less than minimal duration does not cut trip. Default 300 seconds.
*/
- public static final ConfigKey<Long> REPORT_TRIP_MINIMAL_PARKING_DURATION = new ConfigKey<>(
+ public static final ConfigKey<Long> REPORT_TRIP_MINIMAL_PARKING_DURATION = new LongConfigKey(
"report.trip.minimalParkingDuration",
- Collections.singletonList(KeyType.GLOBAL),
+ List.of(KeyType.CONFIG),
300L);
/**
* Gaps of more than specified time are counted as stops. Default value is one hour.
*/
- public static final ConfigKey<Long> REPORT_TRIP_MINIMAL_NO_DATA_DURATION = new ConfigKey<>(
+ public static final ConfigKey<Long> REPORT_TRIP_MINIMAL_NO_DATA_DURATION = new LongConfigKey(
"report.trip.minimalNoDataDuration",
- Collections.singletonList(KeyType.GLOBAL),
+ List.of(KeyType.CONFIG),
3600L);
/**
* Flag to enable ignition use for trips calculation.
*/
- public static final ConfigKey<Boolean> REPORT_TRIP_USE_IGNITION = new ConfigKey<>(
+ public static final ConfigKey<Boolean> REPORT_TRIP_USE_IGNITION = new BooleanConfigKey(
"report.trip.useIgnition",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
+
+ /**
+ * Ignore odometer value reported by the device and use server-calculated total distance instead. This is useful
+ * if device reports invalid or zero odometer values.
+ */
+ public static final ConfigKey<Boolean> REPORT_IGNORE_ODOMETER = new BooleanConfigKey(
+ "report.ignoreOdometer",
+ List.of(KeyType.CONFIG));
/**
* Boolean flag to enable or disable position filtering.
*/
- public static final ConfigKey<Boolean> FILTER_ENABLE = new ConfigKey<>(
+ public static final ConfigKey<Boolean> FILTER_ENABLE = new BooleanConfigKey(
"filter.enable",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Filter invalid (valid field is set to false) positions.
*/
- public static final ConfigKey<Boolean> FILTER_INVALID = new ConfigKey<>(
+ public static final ConfigKey<Boolean> FILTER_INVALID = new BooleanConfigKey(
"filter.invalid",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Filter zero coordinates. Zero latitude and longitude are theoretically valid values, but it practice it usually
* indicates invalid GPS data.
*/
- public static final ConfigKey<Boolean> FILTER_ZERO = new ConfigKey<>(
+ public static final ConfigKey<Boolean> FILTER_ZERO = new BooleanConfigKey(
"filter.zero",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Filter duplicate records (duplicates are detected by time value).
*/
- public static final ConfigKey<Boolean> FILTER_DUPLICATE = new ConfigKey<>(
+ public static final ConfigKey<Boolean> FILTER_DUPLICATE = new BooleanConfigKey(
"filter.duplicate",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
- * Filter records with fix time in future. The values is specified in seconds. Records that have fix time more than
- * specified number of seconds later than current server time would be filtered out.
+ * Filter records with fix time in the future. The value is specified in seconds. Records that have fix time more
+ * than the specified number of seconds later than current server time would be filtered out.
*/
- public static final ConfigKey<Long> FILTER_FUTURE = new ConfigKey<>(
+ public static final ConfigKey<Long> FILTER_FUTURE = new LongConfigKey(
"filter.future",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
+
+ /**
+ * Filter records with fix time in the past. The value is specified in seconds. Records that have fix time more
+ * than the specified number of seconds before current server time would be filtered out.
+ */
+ public static final ConfigKey<Long> FILTER_PAST = new LongConfigKey(
+ "filter.past",
+ List.of(KeyType.CONFIG));
/**
* Filter positions with accuracy less than specified value in meters.
*/
- public static final ConfigKey<Integer> FILTER_ACCURACY = new ConfigKey<>(
+ public static final ConfigKey<Integer> FILTER_ACCURACY = new IntegerConfigKey(
"filter.accuracy",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Filter cell and wifi locations that are coming from geolocation provider.
*/
- public static final ConfigKey<Boolean> FILTER_APPROXIMATE = new ConfigKey<>(
+ public static final ConfigKey<Boolean> FILTER_APPROXIMATE = new BooleanConfigKey(
"filter.approximate",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Filter positions with exactly zero speed values.
*/
- public static final ConfigKey<Boolean> FILTER_STATIC = new ConfigKey<>(
+ public static final ConfigKey<Boolean> FILTER_STATIC = new BooleanConfigKey(
"filter.static",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Filter records by distance. The values is specified in meters. If the new position is less far than this value
* from the last one it gets filtered out.
*/
- public static final ConfigKey<Integer> FILTER_DISTANCE = new ConfigKey<>(
+ public static final ConfigKey<Integer> FILTER_DISTANCE = new IntegerConfigKey(
"filter.distance",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
- * Filter records by Maximum Speed value in knots. Can be used to filter jumps to far locations even if they're
- * marked as valid. Shouldn't be too low. Start testing with values at about 25000.
+ * Filter records by Maximum Speed value in knots. Can be used to filter jumps to far locations even if Position
+ * appears valid or if Position `speed` field reported by the device is also within limits. Calculates speed from
+ * the distance to the previous position and the elapsed time.
+ * Tip: Shouldn't be too low. Start testing with values at about 25000.
*/
- public static final ConfigKey<Integer> FILTER_MAX_SPEED = new ConfigKey<>(
+ public static final ConfigKey<Integer> FILTER_MAX_SPEED = new IntegerConfigKey(
"filter.maxSpeed",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Filter position if time from previous position is less than specified value in seconds.
*/
- public static final ConfigKey<Integer> FILTER_MIN_PERIOD = new ConfigKey<>(
+ public static final ConfigKey<Integer> FILTER_MIN_PERIOD = new IntegerConfigKey(
"filter.minPeriod",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
+
+ /**
+ * If false, the server expects all locations to come sequentially (for each device). Filter checks for duplicates,
+ * distance, speed, or time period only against the location that was last received by server.
+ * If true, the server expects locations to come at random order (since tracking device might go offline).
+ * Filter checks for duplicates, distance, speed, or time period against the preceding Position's.
+ * Important: setting to true can cause potential performance issues.
+ */
+ public static final ConfigKey<Boolean> FILTER_RELATIVE = new BooleanConfigKey(
+ "filter.relative",
+ List.of(KeyType.CONFIG));
/**
- * Time limit for the filtering in seconds. If the time difference between last position and a new one is more than
- * this limit, the new position will not be filtered out.
+ * Time limit for the filtering in seconds. If the time difference between the last position was received by server
+ * and a new position is received by server is more than this limit, the new position will not be filtered out.
*/
- public static final ConfigKey<Long> FILTER_SKIP_LIMIT = new ConfigKey<>(
+ public static final ConfigKey<Long> FILTER_SKIP_LIMIT = new LongConfigKey(
"filter.skipLimit",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Enable attributes skipping. Attribute skipping can be enabled in the config or device attributes.
+ * If position contains any attribute mentioned in "filter.skipAttributes" config key, position is not filtered out.
*/
- public static final ConfigKey<Boolean> FILTER_SKIP_ATTRIBUTES_ENABLE = new ConfigKey<>(
+ public static final ConfigKey<Boolean> FILTER_SKIP_ATTRIBUTES_ENABLE = new BooleanConfigKey(
"filter.skipAttributes.enable",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
+
+ /**
+ * Attribute skipping can be enabled in the config or device attributes.
+ * If position contains any attribute mentioned in "filter.skipAttributes" config key, position is not filtered out.
+ */
+ public static final ConfigKey<String> FILTER_SKIP_ATTRIBUTES = new StringConfigKey(
+ "filter.skipAttributes",
+ List.of(KeyType.CONFIG, KeyType.DEVICE),
+ "");
/**
* Override device time. Possible values are 'deviceTime' and 'serverTime'
*/
- public static final ConfigKey<String> TIME_OVERRIDE = new ConfigKey<>(
+ public static final ConfigKey<String> TIME_OVERRIDE = new StringConfigKey(
"time.override",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
+
+ /**
+ * List of protocols to enable. If not specified, Traccar enabled all protocols that have port numbers listed.
+ * The value is a comma-separated list of protocol names.
+ * Example value: teltonika,osmand
+ */
+ public static final ConfigKey<String> PROTOCOLS_ENABLE = new StringConfigKey(
+ "protocols.enable",
+ List.of(KeyType.CONFIG));
/**
* List of protocols for overriding time. If not specified override is applied globally. List consist of protocol
* names that can be separated by comma or single space character.
*/
- public static final ConfigKey<String> TIME_PROTOCOLS = new ConfigKey<>(
+ public static final ConfigKey<String> TIME_PROTOCOLS = new StringConfigKey(
"time.protocols",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Replaces coordinates with last known if change is less than a 'coordinates.minError' meters
* or more than a 'coordinates.maxError' meters. Helps to avoid coordinates jumps during parking period
* or jumps to zero coordinates.
*/
- public static final ConfigKey<Boolean> COORDINATES_FILTER = new ConfigKey<>(
+ public static final ConfigKey<Boolean> COORDINATES_FILTER = new BooleanConfigKey(
"coordinates.filter",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Distance in meters. Distances below this value gets handled like explained in 'coordinates.filter'.
*/
- public static final ConfigKey<Integer> COORDINATES_MIN_ERROR = new ConfigKey<>(
+ public static final ConfigKey<Integer> COORDINATES_MIN_ERROR = new IntegerConfigKey(
"coordinates.minError",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Distance in meters. Distances above this value gets handled like explained in 'coordinates.filter', but only if
* Position is also marked as 'invalid'.
*/
- public static final ConfigKey<Integer> COORDINATES_MAX_ERROR = new ConfigKey<>(
+ public static final ConfigKey<Integer> COORDINATES_MAX_ERROR = new IntegerConfigKey(
"coordinates.maxError",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Enable to save device IP addresses information. Disabled by default.
*/
- public static final ConfigKey<Boolean> PROCESSING_REMOTE_ADDRESS_ENABLE = new ConfigKey<>(
+ public static final ConfigKey<Boolean> PROCESSING_REMOTE_ADDRESS_ENABLE = new BooleanConfigKey(
"processing.remoteAddress.enable",
- Collections.singletonList(KeyType.GLOBAL));
-
- /**
- * Enable engine hours calculation on the server side. It uses ignition value to determine engine state.
- */
- public static final ConfigKey<Boolean> PROCESSING_ENGINE_HOURS_ENABLE = new ConfigKey<>(
- "processing.engineHours.enable",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Enable copying of missing attributes from last position to the current one. Might be useful if device doesn't
* send some values in every message.
*/
- public static final ConfigKey<Boolean> PROCESSING_COPY_ATTRIBUTES_ENABLE = new ConfigKey<>(
+ public static final ConfigKey<Boolean> PROCESSING_COPY_ATTRIBUTES_ENABLE = new BooleanConfigKey(
"processing.copyAttributes.enable",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
- * Enable computed attributes processing.
+ * List of attributes to copy. Attributes should be separated by a comma without any spacing.
+ * For example: alarm,ignition
*/
- public static final ConfigKey<Boolean> PROCESSING_COMPUTED_ATTRIBUTES_ENABLE = new ConfigKey<>(
- "processing.computedAttributes.enable",
- Collections.singletonList(KeyType.GLOBAL));
+ public static final ConfigKey<String> PROCESSING_COPY_ATTRIBUTES = new StringConfigKey(
+ "processing.copyAttributes",
+ List.of(KeyType.CONFIG, KeyType.DEVICE));
/**
* Enable computed attributes processing.
*/
- public static final ConfigKey<Boolean> PROCESSING_COMPUTED_ATTRIBUTES_DEVICE_ATTRIBUTES = new ConfigKey<>(
+ public static final ConfigKey<Boolean> PROCESSING_COMPUTED_ATTRIBUTES_DEVICE_ATTRIBUTES = new BooleanConfigKey(
"processing.computedAttributes.deviceAttributes",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Boolean flag to enable or disable reverse geocoder.
*/
- public static final ConfigKey<Boolean> GEOCODER_ENABLE = new ConfigKey<>(
+ public static final ConfigKey<Boolean> GEOCODER_ENABLE = new BooleanConfigKey(
"geocoder.enable",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Reverse geocoder type. Check reverse geocoding documentation for more info. By default (if the value is not
* specified) server uses Google API.
*/
- public static final ConfigKey<String> GEOCODER_TYPE = new ConfigKey<>(
+ public static final ConfigKey<String> GEOCODER_TYPE = new StringConfigKey(
"geocoder.type",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Geocoder server URL. Applicable only to Nominatim and Gisgraphy providers.
*/
- public static final ConfigKey<String> GEOCODER_URL = new ConfigKey<>(
+ public static final ConfigKey<String> GEOCODER_URL = new StringConfigKey(
"geocoder.url",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* App id for use with Here provider.
*/
- public static final ConfigKey<String> GEOCODER_ID = new ConfigKey<>(
+ public static final ConfigKey<String> GEOCODER_ID = new StringConfigKey(
"geocoder.id",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Provider API key. Most providers require API keys.
*/
- public static final ConfigKey<String> GEOCODER_KEY = new ConfigKey<>(
+ public static final ConfigKey<String> GEOCODER_KEY = new StringConfigKey(
"geocoder.key",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Language parameter for providers that support localization (e.g. Google and Nominatim).
*/
- public static final ConfigKey<String> GEOCODER_LANGUAGE = new ConfigKey<>(
+ public static final ConfigKey<String> GEOCODER_LANGUAGE = new StringConfigKey(
"geocoder.language",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Address format string. Default value is %h %r, %t, %s, %c. See AddressFormat for more info.
*/
- public static final ConfigKey<String> GEOCODER_FORMAT = new ConfigKey<>(
+ public static final ConfigKey<String> GEOCODER_FORMAT = new StringConfigKey(
"geocoder.format",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Cache size for geocoding results.
*/
- public static final ConfigKey<Integer> GEOCODER_CACHE_SIZE = new ConfigKey<>(
+ public static final ConfigKey<Integer> GEOCODER_CACHE_SIZE = new IntegerConfigKey(
"geocoder.cacheSize",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Disable automatic reverse geocoding requests for all positions.
*/
- public static final ConfigKey<Boolean> GEOCODER_IGNORE_POSITIONS = new ConfigKey<>(
+ public static final ConfigKey<Boolean> GEOCODER_IGNORE_POSITIONS = new BooleanConfigKey(
"geocoder.ignorePositions",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Boolean flag to apply reverse geocoding to invalid positions.
*/
- public static final ConfigKey<Boolean> GEOCODER_PROCESS_INVALID_POSITIONS = new ConfigKey<>(
+ public static final ConfigKey<Boolean> GEOCODER_PROCESS_INVALID_POSITIONS = new BooleanConfigKey(
"geocoder.processInvalidPositions",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Optional parameter to specify minimum distance for new reverse geocoding request. If distance is less than
* specified value (in meters), then Traccar will reuse last known address.
*/
- public static final ConfigKey<Integer> GEOCODER_REUSE_DISTANCE = new ConfigKey<>(
+ public static final ConfigKey<Integer> GEOCODER_REUSE_DISTANCE = new IntegerConfigKey(
"geocoder.reuseDistance",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Perform geocoding when preparing reports and sending notifications.
*/
- public static final ConfigKey<Boolean> GEOCODER_ON_REQUEST = new ConfigKey<>(
+ public static final ConfigKey<Boolean> GEOCODER_ON_REQUEST = new BooleanConfigKey(
"geocoder.onRequest",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Boolean flag to enable LBS location resolution. Some devices send cell towers information and WiFi point when GPS
* location is not available. Traccar can determine coordinates based on that information using third party
* services. Default value is false.
*/
- public static final ConfigKey<Boolean> GEOLOCATION_ENABLE = new ConfigKey<>(
+ public static final ConfigKey<Boolean> GEOLOCATION_ENABLE = new BooleanConfigKey(
"geolocation.enable",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Provider to use for LBS location. Available options: google, mozilla and opencellid. By default opencellid is
* used. You have to supply a key that you get from corresponding provider. For more information see LBS geolocation
* documentation.
*/
- public static final ConfigKey<String> GEOLOCATION_TYPE = new ConfigKey<>(
+ public static final ConfigKey<String> GEOLOCATION_TYPE = new StringConfigKey(
"geolocation.type",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Geolocation provider API URL address. Not required for most providers.
*/
- public static final ConfigKey<String> GEOLOCATION_URL = new ConfigKey<>(
+ public static final ConfigKey<String> GEOLOCATION_URL = new StringConfigKey(
"geolocation.url",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Provider API key. OpenCellID service requires API key.
*/
- public static final ConfigKey<String> GEOLOCATION_KEY = new ConfigKey<>(
+ public static final ConfigKey<String> GEOLOCATION_KEY = new StringConfigKey(
"geolocation.key",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Boolean flag to apply geolocation to invalid positions.
*/
- public static final ConfigKey<Boolean> GEOLOCATION_PROCESS_INVALID_POSITIONS = new ConfigKey<>(
+ public static final ConfigKey<Boolean> GEOLOCATION_PROCESS_INVALID_POSITIONS = new BooleanConfigKey(
"geolocation.processInvalidPositions",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
+
+ /**
+ * Reuse last geolocation result if network details have not changed.
+ */
+ public static final ConfigKey<Boolean> GEOLOCATION_REUSE = new BooleanConfigKey(
+ "geolocation.reuse",
+ List.of(KeyType.CONFIG));
/**
* Default MCC value to use if device doesn't report MCC.
*/
- public static final ConfigKey<Integer> GEOLOCATION_MCC = new ConfigKey<>(
+ public static final ConfigKey<Integer> GEOLOCATION_MCC = new IntegerConfigKey(
"geolocation.mcc",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Default MNC value to use if device doesn't report MNC.
*/
- public static final ConfigKey<Integer> GEOLOCATION_MNC = new ConfigKey<>(
+ public static final ConfigKey<Integer> GEOLOCATION_MNC = new IntegerConfigKey(
"geolocation.mnc",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Boolean flag to enable speed limit API to get speed limit values depending on location. Default value is false.
*/
- public static final ConfigKey<Boolean> SPEED_LIMIT_ENABLE = new ConfigKey<>(
+ public static final ConfigKey<Boolean> SPEED_LIMIT_ENABLE = new BooleanConfigKey(
"speedLimit.enable",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Provider to use for speed limit. Available options: overpass. By default overpass is used.
*/
- public static final ConfigKey<String> SPEED_LIMIT_TYPE = new ConfigKey<>(
+ public static final ConfigKey<String> SPEED_LIMIT_TYPE = new StringConfigKey(
"speedLimit.type",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Speed limit provider API URL address.
*/
- public static final ConfigKey<String> SPEED_LIMIT_URL = new ConfigKey<>(
+ public static final ConfigKey<String> SPEED_LIMIT_URL = new StringConfigKey(
"speedLimit.url",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Override latitude sign / hemisphere. Useful in cases where value is incorrect because of device bug. Value can be
* N for North or S for South.
*/
- public static final ConfigKey<String> LOCATION_LATITUDE_HEMISPHERE = new ConfigKey<>(
+ public static final ConfigKey<String> LOCATION_LATITUDE_HEMISPHERE = new StringConfigKey(
"location.latitudeHemisphere",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Override longitude sign / hemisphere. Useful in cases where value is incorrect because of device bug. Value can
* be E for East or W for West.
*/
- public static final ConfigKey<String> LOCATION_LONGITUDE_HEMISPHERE = new ConfigKey<>(
+ public static final ConfigKey<String> LOCATION_LONGITUDE_HEMISPHERE = new StringConfigKey(
"location.longitudeHemisphere",
- Collections.singletonList(KeyType.GLOBAL));
-
- /**
- * Enable Jetty Request Log.
- */
- public static final ConfigKey<Boolean> WEB_REQUEST_LOG_ENABLE = new ConfigKey<>(
- "web.requestLog.enable",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Jetty Request Log Path.
@@ -1186,82 +1489,118 @@ public final class Keys {
* over the file.
* Example: ./logs/jetty-yyyy_mm_dd.request.log
*/
- public static final ConfigKey<String> WEB_REQUEST_LOG_PATH = new ConfigKey<>(
+ public static final ConfigKey<String> WEB_REQUEST_LOG_PATH = new StringConfigKey(
"web.requestLog.path",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Set the number of days before rotated request log files are deleted.
*/
- public static final ConfigKey<Integer> WEB_REQUEST_LOG_RETAIN_DAYS = new ConfigKey<>(
+ public static final ConfigKey<Integer> WEB_REQUEST_LOG_RETAIN_DAYS = new IntegerConfigKey(
"web.requestLog.retainDays",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Disable systemd health checks.
*/
- public static final ConfigKey<Boolean> WEB_DISABLE_HEALTH_CHECK = new ConfigKey<>(
+ public static final ConfigKey<Boolean> WEB_DISABLE_HEALTH_CHECK = new BooleanConfigKey(
"web.disableHealthCheck",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Sets SameSite cookie attribute value.
* Supported options: Lax, Strict, None.
*/
- public static final ConfigKey<String> WEB_SAME_SITE_COOKIE = new ConfigKey<>(
+ public static final ConfigKey<String> WEB_SAME_SITE_COOKIE = new StringConfigKey(
"web.sameSiteCookie",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Enables persisting Jetty session to the database
*/
- public static final ConfigKey<Boolean> WEB_PERSIST_SESSION = new ConfigKey<>(
+ public static final ConfigKey<Boolean> WEB_PERSIST_SESSION = new BooleanConfigKey(
"web.persistSession",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
+
+ /**
+ * Public URL for the web app. Used for notification and report link.
+ * If not provided, Traccar will attempt to get a URL from the server IP address, but it might be a local address.
+ */
+ public static final ConfigKey<String> WEB_URL = new StringConfigKey(
+ "web.url",
+ List.of(KeyType.CONFIG));
/**
* Output logging to the standard terminal output instead of a log file.
*/
- public static final ConfigKey<Boolean> LOGGER_CONSOLE = new ConfigKey<>(
+ public static final ConfigKey<Boolean> LOGGER_CONSOLE = new BooleanConfigKey(
"logger.console",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
+
+ /**
+ * Log executed SQL queries.
+ */
+ public static final ConfigKey<Boolean> LOGGER_QUERIES = new BooleanConfigKey(
+ "logger.queries",
+ List.of(KeyType.CONFIG));
/**
* Log file name. For rotating logs, a date is added at the end of the file name for non-current logs.
*/
- public static final ConfigKey<String> LOGGER_FILE = new ConfigKey<>(
+ public static final ConfigKey<String> LOGGER_FILE = new StringConfigKey(
"logger.file",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Logging level. Default value is 'info'.
* Available options: off, severe, warning, info, config, fine, finer, finest, all.
*/
- public static final ConfigKey<String> LOGGER_LEVEL = new ConfigKey<>(
+ public static final ConfigKey<String> LOGGER_LEVEL = new StringConfigKey(
"logger.level",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Print full exception traces. Useful for debugging. By default shortened traces are logged.
*/
- public static final ConfigKey<Boolean> LOGGER_FULL_STACK_TRACES = new ConfigKey<>(
+ public static final ConfigKey<Boolean> LOGGER_FULL_STACK_TRACES = new BooleanConfigKey(
"logger.fullStackTraces",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* Create a new log file daily. Helps with log management. For example, downloading and cleaning logs. Enabled by
* default.
*/
- public static final ConfigKey<Boolean> LOGGER_ROTATE = new ConfigKey<>(
+ public static final ConfigKey<Boolean> LOGGER_ROTATE = new BooleanConfigKey(
"logger.rotate",
- Collections.singletonList(KeyType.GLOBAL));
+ List.of(KeyType.CONFIG));
/**
* A list of position attributes to log.
*/
- public static final ConfigKey<String> LOGGER_ATTRIBUTES = new ConfigKey<>(
+ public static final ConfigKey<String> LOGGER_ATTRIBUTES = new StringConfigKey(
"logger.attributes",
- Collections.singletonList(KeyType.GLOBAL),
+ List.of(KeyType.CONFIG),
"time,position,speed,course,accuracy,result");
+ /**
+ * Multicast interface. It can be either an IP address or an interface name.
+ */
+ public static final ConfigKey<String> BROADCAST_INTERFACE = new StringConfigKey(
+ "broadcast.interface",
+ List.of(KeyType.CONFIG));
+
+ /**
+ * Multicast address for broadcasting synchronization events.
+ */
+ public static final ConfigKey<String> BROADCAST_ADDRESS = new StringConfigKey(
+ "broadcast.address",
+ List.of(KeyType.CONFIG));
+
+ /**
+ * Multicast port for broadcasting synchronization events.
+ */
+ public static final ConfigKey<Integer> BROADCAST_PORT = new IntegerConfigKey(
+ "broadcast.port",
+ List.of(KeyType.CONFIG));
+
}
diff --git a/src/main/java/org/traccar/database/AttributesManager.java b/src/main/java/org/traccar/database/AttributesManager.java
deleted file mode 100644
index 28816645a..000000000
--- a/src/main/java/org/traccar/database/AttributesManager.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
- * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.database;
-
-import org.traccar.model.Attribute;
-
-public class AttributesManager extends ExtendedObjectManager<Attribute> {
-
- public AttributesManager(DataManager dataManager) {
- super(dataManager, Attribute.class);
- }
-
- @Override
- public void updateCachedItem(Attribute attribute) {
- Attribute cachedAttribute = getById(attribute.getId());
- cachedAttribute.setDescription(attribute.getDescription());
- cachedAttribute.setAttribute(attribute.getAttribute());
- cachedAttribute.setExpression(attribute.getExpression());
- cachedAttribute.setType(attribute.getType());
- }
-
-}
diff --git a/src/main/java/org/traccar/database/BaseObjectManager.java b/src/main/java/org/traccar/database/BaseObjectManager.java
deleted file mode 100644
index be6310033..000000000
--- a/src/main/java/org/traccar/database/BaseObjectManager.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
- * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.database;
-
-import java.sql.SQLException;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.traccar.model.BaseModel;
-
-public class BaseObjectManager<T extends BaseModel> {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(BaseObjectManager.class);
-
- private final ReadWriteLock lock = new ReentrantReadWriteLock();
-
- private final DataManager dataManager;
-
- private final Class<T> baseClass;
- private Map<Long, T> items;
-
- protected BaseObjectManager(DataManager dataManager, Class<T> baseClass) {
- this.dataManager = dataManager;
- this.baseClass = baseClass;
- refreshItems();
- }
-
- protected final void readLock() {
- lock.readLock().lock();
- }
-
- protected final void readUnlock() {
- lock.readLock().unlock();
- }
-
- protected final void writeLock() {
- lock.writeLock().lock();
- }
-
- protected final void writeUnlock() {
- lock.writeLock().unlock();
- }
-
- protected final DataManager getDataManager() {
- return dataManager;
- }
-
- protected final Class<T> getBaseClass() {
- return baseClass;
- }
-
- public T getById(long itemId) {
- try {
- readLock();
- return items.get(itemId);
- } finally {
- readUnlock();
- }
- }
-
- public void refreshItems() {
- if (dataManager != null) {
- try {
- writeLock();
- Collection<T> databaseItems = dataManager.getObjects(baseClass);
- if (items == null) {
- items = new ConcurrentHashMap<>(databaseItems.size());
- }
- Set<Long> databaseItemIds = new HashSet<>();
- for (T item : databaseItems) {
- databaseItemIds.add(item.getId());
- if (items.containsKey(item.getId())) {
- updateCachedItem(item);
- } else {
- addNewItem(item);
- }
- }
- for (Long cachedItemId : items.keySet()) {
- if (!databaseItemIds.contains(cachedItemId)) {
- removeCachedItem(cachedItemId);
- }
- }
- } catch (SQLException error) {
- LOGGER.warn("Error refreshing items", error);
- } finally {
- writeUnlock();
- }
- }
- }
-
- protected void addNewItem(T item) {
- try {
- writeLock();
- items.put(item.getId(), item);
- } finally {
- writeUnlock();
- }
- }
-
- public void addItem(T item) throws SQLException {
- dataManager.addObject(item);
- addNewItem(item);
- }
-
- protected void updateCachedItem(T item) {
- try {
- writeLock();
- items.put(item.getId(), item);
- } finally {
- writeUnlock();
- }
- }
-
- public void updateItem(T item) throws SQLException {
- dataManager.updateObject(item);
- updateCachedItem(item);
- }
-
- protected void removeCachedItem(long itemId) {
- try {
- writeLock();
- items.remove(itemId);
- } finally {
- writeUnlock();
- }
- }
-
- public void removeItem(long itemId) throws SQLException {
- BaseModel item = getById(itemId);
- if (item != null) {
- dataManager.removeObject(baseClass, itemId);
- removeCachedItem(itemId);
- }
- }
-
- public final Collection<T> getItems(Set<Long> itemIds) {
- Collection<T> result = new LinkedList<>();
- for (long itemId : itemIds) {
- T item = getById(itemId);
- if (item != null) {
- result.add(item);
- }
- }
- return result;
- }
-
- public Set<Long> getAllItems() {
- try {
- readLock();
- return items.keySet();
- } finally {
- readUnlock();
- }
- }
-
-}
diff --git a/src/main/java/org/traccar/database/CommandsManager.java b/src/main/java/org/traccar/database/CommandsManager.java
index 843c89e82..df399cd7a 100644
--- a/src/main/java/org/traccar/database/CommandsManager.java
+++ b/src/main/java/org/traccar/database/CommandsManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,177 +16,112 @@
*/
package org.traccar.database;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentLinkedQueue;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.traccar.BaseProtocol;
-import org.traccar.Context;
+import org.traccar.ServerManager;
+import org.traccar.broadcast.BroadcastInterface;
+import org.traccar.broadcast.BroadcastService;
import org.traccar.model.Command;
-import org.traccar.model.Typed;
+import org.traccar.model.Device;
import org.traccar.model.Position;
-
-public class CommandsManager extends ExtendedObjectManager<Command> {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(CommandsManager.class);
-
- private final Map<Long, Queue<Command>> deviceQueues = new ConcurrentHashMap<>();
-
- private final boolean queueing;
-
- public CommandsManager(DataManager dataManager, boolean queueing) {
- super(dataManager, Command.class);
- this.queueing = queueing;
- }
-
- public boolean checkDeviceCommand(long deviceId, long commandId) {
- return !getAllDeviceItems(deviceId).contains(commandId);
+import org.traccar.model.QueuedCommand;
+import org.traccar.session.ConnectionManager;
+import org.traccar.session.DeviceSession;
+import org.traccar.sms.SmsManager;
+import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Order;
+import org.traccar.storage.query.Request;
+
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.util.Collection;
+import java.util.stream.Collectors;
+
+@Singleton
+public class CommandsManager implements BroadcastInterface {
+
+ private final Storage storage;
+ private final ServerManager serverManager;
+ private final SmsManager smsManager;
+ private final ConnectionManager connectionManager;
+ private final BroadcastService broadcastService;
+
+ @Inject
+ public CommandsManager(
+ Storage storage, ServerManager serverManager, @Nullable SmsManager smsManager,
+ ConnectionManager connectionManager, BroadcastService broadcastService) {
+ this.storage = storage;
+ this.serverManager = serverManager;
+ this.smsManager = smsManager;
+ this.connectionManager = connectionManager;
+ this.broadcastService = broadcastService;
+ broadcastService.registerListener(this);
}
public boolean sendCommand(Command command) throws Exception {
long deviceId = command.getDeviceId();
- if (command.getId() != 0) {
- command = getById(command.getId()).clone();
- command.setDeviceId(deviceId);
- }
if (command.getTextChannel()) {
- Position lastPosition = Context.getIdentityManager().getLastPosition(deviceId);
- String phone = Context.getIdentityManager().getById(deviceId).getPhone();
- if (lastPosition != null) {
- BaseProtocol protocol = Context.getServerManager().getProtocol(lastPosition.getProtocol());
- protocol.sendTextCommand(phone, command);
+ if (smsManager == null) {
+ throw new RuntimeException("SMS not configured");
+ }
+ Device device = storage.getObject(Device.class, new Request(
+ new Columns.Include("positionId", "phone"), new Condition.Equals("id", deviceId)));
+ Position position = storage.getObject(Position.class, new Request(
+ new Columns.All(), new Condition.Equals("id", device.getPositionId())));
+ if (position != null) {
+ BaseProtocol protocol = serverManager.getProtocol(position.getProtocol());
+ protocol.sendTextCommand(device.getPhone(), command);
} else if (command.getType().equals(Command.TYPE_CUSTOM)) {
- if (Context.getSmsManager() != null) {
- Context.getSmsManager().sendMessageSync(phone, command.getString(Command.KEY_DATA), true);
- } else {
- throw new RuntimeException("SMS is not enabled");
- }
+ smsManager.sendMessage(device.getPhone(), command.getString(Command.KEY_DATA), true);
} else {
throw new RuntimeException("Command " + command.getType() + " is not supported");
}
} else {
- ActiveDevice activeDevice = Context.getConnectionManager().getActiveDevice(deviceId);
- if (activeDevice != null) {
- if (activeDevice.supportsLiveCommands()) {
- activeDevice.sendCommand(command);
- } else {
- getDeviceQueue(deviceId).add(command);
- return false;
- }
- } else if (!queueing) {
- throw new RuntimeException("Device is not online");
+ DeviceSession deviceSession = connectionManager.getDeviceSession(deviceId);
+ if (deviceSession != null && deviceSession.supportsLiveCommands()) {
+ deviceSession.sendCommand(command);
} else {
- getDeviceQueue(deviceId).add(command);
+ storage.addObject(QueuedCommand.fromCommand(command), new Request(new Columns.Exclude("id")));
+ broadcastService.updateCommand(true, deviceId);
return false;
}
}
return true;
}
- public Collection<Long> getSupportedCommands(long deviceId) {
- List<Long> result = new ArrayList<>();
- Position lastPosition = Context.getIdentityManager().getLastPosition(deviceId);
- for (long commandId : getAllDeviceItems(deviceId)) {
- Command command = getById(commandId);
- if (lastPosition != null) {
- BaseProtocol protocol = Context.getServerManager().getProtocol(lastPosition.getProtocol());
- if (command.getTextChannel() && protocol.getSupportedTextCommands().contains(command.getType())
- || !command.getTextChannel()
- && protocol.getSupportedDataCommands().contains(command.getType())) {
- result.add(commandId);
- }
- } else if (command.getType().equals(Command.TYPE_CUSTOM)) {
- result.add(commandId);
- }
- }
- return result;
- }
-
- public Collection<Typed> getCommandTypes(long deviceId, boolean textChannel) {
- Position lastPosition = Context.getIdentityManager().getLastPosition(deviceId);
- if (lastPosition != null) {
- return getCommandTypes(lastPosition.getProtocol(), textChannel);
- } else {
- return Collections.singletonList(new Typed(Command.TYPE_CUSTOM));
- }
- }
-
- public Collection<Typed> getCommandTypes(String protocolName, boolean textChannel) {
- List<Typed> result = new ArrayList<>();
- BaseProtocol protocol = Context.getServerManager().getProtocol(protocolName);
- Collection<String> commands;
- commands = textChannel ? protocol.getSupportedTextCommands() : protocol.getSupportedDataCommands();
- for (String commandKey : commands) {
- result.add(new Typed(commandKey));
- }
- return result;
- }
-
- public Collection<Typed> getAllCommandTypes() {
- List<Typed> result = new ArrayList<>();
- Field[] fields = Command.class.getDeclaredFields();
- for (Field field : fields) {
- if (Modifier.isStatic(field.getModifiers()) && field.getName().startsWith("TYPE_")) {
- try {
- result.add(new Typed(field.get(null).toString()));
- } catch (IllegalArgumentException | IllegalAccessException error) {
- LOGGER.warn("Get command types error", error);
- }
- }
- }
- return result;
- }
-
- private Queue<Command> getDeviceQueue(long deviceId) {
- Queue<Command> deviceQueue;
- try {
- readLock();
- deviceQueue = deviceQueues.get(deviceId);
- } finally {
- readUnlock();
- }
- if (deviceQueue != null) {
- return deviceQueue;
- } else {
- try {
- writeLock();
- return deviceQueues.computeIfAbsent(deviceId, key -> new ConcurrentLinkedQueue<>());
- } finally {
- writeUnlock();
- }
- }
- }
-
public Collection<Command> readQueuedCommands(long deviceId) {
return readQueuedCommands(deviceId, Integer.MAX_VALUE);
}
public Collection<Command> readQueuedCommands(long deviceId, int count) {
- Queue<Command> deviceQueue;
try {
- readLock();
- deviceQueue = deviceQueues.get(deviceId);
- } finally {
- readUnlock();
+ var commands = storage.getObjects(QueuedCommand.class, new Request(
+ new Columns.All(),
+ new Condition.Equals("deviceId", deviceId),
+ new Order("id", false, count)));
+ for (var command : commands) {
+ storage.removeObject(QueuedCommand.class, new Request(
+ new Condition.Equals("id", command.getId())));
+ }
+ return commands.stream().map(QueuedCommand::toCommand).collect(Collectors.toList());
+ } catch (StorageException e) {
+ throw new RuntimeException(e);
}
- Collection<Command> result = new ArrayList<>();
- if (deviceQueue != null) {
- Command command = deviceQueue.poll();
- while (command != null && result.size() < count) {
- result.add(command);
- command = deviceQueue.poll();
+ }
+
+ @Override
+ public void updateCommand(boolean local, long deviceId) {
+ if (!local) {
+ DeviceSession deviceSession = connectionManager.getDeviceSession(deviceId);
+ if (deviceSession != null && deviceSession.supportsLiveCommands()) {
+ for (Command command : readQueuedCommands(deviceId)) {
+ deviceSession.sendCommand(command);
+ }
}
}
- return result;
}
}
diff --git a/src/main/java/org/traccar/database/ConnectionManager.java b/src/main/java/org/traccar/database/ConnectionManager.java
deleted file mode 100644
index e12d44612..000000000
--- a/src/main/java/org/traccar/database/ConnectionManager.java
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.database;
-
-import io.netty.channel.Channel;
-import io.netty.util.Timeout;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.traccar.Context;
-import org.traccar.GlobalTimer;
-import org.traccar.Main;
-import org.traccar.Protocol;
-import org.traccar.config.Keys;
-import org.traccar.handler.events.MotionEventHandler;
-import org.traccar.handler.events.OverspeedEventHandler;
-import org.traccar.model.Device;
-import org.traccar.model.DeviceState;
-import org.traccar.model.Event;
-import org.traccar.model.Position;
-
-import java.net.SocketAddress;
-import java.sql.SQLException;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.TimeUnit;
-
-public class ConnectionManager {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(ConnectionManager.class);
-
- private final long deviceTimeout;
- private final boolean updateDeviceState;
-
- private final Map<Long, ActiveDevice> activeDevices = new ConcurrentHashMap<>();
- private final Map<Long, Set<UpdateListener>> listeners = new ConcurrentHashMap<>();
- private final Map<Long, Timeout> timeouts = new ConcurrentHashMap<>();
-
- public ConnectionManager() {
- deviceTimeout = Context.getConfig().getLong(Keys.STATUS_TIMEOUT) * 1000;
- updateDeviceState = Context.getConfig().getBoolean(Keys.STATUS_UPDATE_DEVICE_STATE);
- }
-
- public void addActiveDevice(long deviceId, Protocol protocol, Channel channel, SocketAddress remoteAddress) {
- activeDevices.put(deviceId, new ActiveDevice(deviceId, protocol, channel, remoteAddress));
- }
-
- public void removeActiveDevice(Channel channel) {
- for (ActiveDevice activeDevice : activeDevices.values()) {
- if (activeDevice.getChannel() == channel) {
- updateDevice(activeDevice.getDeviceId(), Device.STATUS_OFFLINE, null);
- activeDevices.remove(activeDevice.getDeviceId());
- break;
- }
- }
- }
-
- public ActiveDevice getActiveDevice(long deviceId) {
- return activeDevices.get(deviceId);
- }
-
- public void updateDevice(final long deviceId, String status, Date time) {
- Device device = Context.getIdentityManager().getById(deviceId);
- if (device == null) {
- return;
- }
-
- String oldStatus = device.getStatus();
- device.setStatus(status);
-
- if (!status.equals(oldStatus)) {
- String eventType;
- Map<Event, Position> events = new HashMap<>();
- switch (status) {
- case Device.STATUS_ONLINE:
- eventType = Event.TYPE_DEVICE_ONLINE;
- break;
- case Device.STATUS_UNKNOWN:
- eventType = Event.TYPE_DEVICE_UNKNOWN;
- if (updateDeviceState) {
- events.putAll(updateDeviceState(deviceId));
- }
- break;
- default:
- eventType = Event.TYPE_DEVICE_OFFLINE;
- if (updateDeviceState) {
- events.putAll(updateDeviceState(deviceId));
- }
- break;
- }
- events.put(new Event(eventType, deviceId), null);
- Context.getNotificationManager().updateEvents(events);
- }
-
- Timeout timeout = timeouts.remove(deviceId);
- if (timeout != null) {
- timeout.cancel();
- }
-
- if (time != null) {
- device.setLastUpdate(time);
- }
-
- if (status.equals(Device.STATUS_ONLINE)) {
- timeouts.put(deviceId, GlobalTimer.getTimer().newTimeout(timeout1 -> {
- if (!timeout1.isCancelled()) {
- updateDevice(deviceId, Device.STATUS_UNKNOWN, null);
- }
- }, deviceTimeout, TimeUnit.MILLISECONDS));
- }
-
- try {
- Context.getDeviceManager().updateDeviceStatus(device);
- } catch (SQLException error) {
- LOGGER.warn("Update device status error", error);
- }
-
- updateDevice(device);
- }
-
- public Map<Event, Position> updateDeviceState(long deviceId) {
- DeviceState deviceState = Context.getDeviceManager().getDeviceState(deviceId);
- Map<Event, Position> result = new HashMap<>();
-
- Map<Event, Position> event = Main.getInjector()
- .getInstance(MotionEventHandler.class).updateMotionState(deviceState);
- if (event != null) {
- result.putAll(event);
- }
-
- event = Main.getInjector().getInstance(OverspeedEventHandler.class)
- .updateOverspeedState(deviceState, Context.getDeviceManager().
- lookupAttributeDouble(deviceId, OverspeedEventHandler.ATTRIBUTE_SPEED_LIMIT, 0, true, false));
- if (event != null) {
- result.putAll(event);
- }
-
- return result;
- }
-
- public synchronized void sendKeepalive() {
- for (Set<UpdateListener> userListeners : listeners.values()) {
- for (UpdateListener listener : userListeners) {
- listener.onKeepalive();
- }
- }
- }
-
- public synchronized void updateDevice(Device device) {
- for (long userId : Context.getPermissionsManager().getDeviceUsers(device.getId())) {
- if (listeners.containsKey(userId)) {
- for (UpdateListener listener : listeners.get(userId)) {
- listener.onUpdateDevice(device);
- }
- }
- }
- }
-
- public synchronized void updatePosition(Position position) {
- long deviceId = position.getDeviceId();
-
- for (long userId : Context.getPermissionsManager().getDeviceUsers(deviceId)) {
- if (listeners.containsKey(userId)) {
- for (UpdateListener listener : listeners.get(userId)) {
- listener.onUpdatePosition(position);
- }
- }
- }
- }
-
- public synchronized void updateEvent(long userId, Event event) {
- if (listeners.containsKey(userId)) {
- for (UpdateListener listener : listeners.get(userId)) {
- listener.onUpdateEvent(event);
- }
- }
- }
-
- public interface UpdateListener {
- void onKeepalive();
- void onUpdateDevice(Device device);
- void onUpdatePosition(Position position);
- void onUpdateEvent(Event event);
- }
-
- public synchronized void addListener(long userId, UpdateListener listener) {
- if (!listeners.containsKey(userId)) {
- listeners.put(userId, new HashSet<>());
- }
- listeners.get(userId).add(listener);
- }
-
- public synchronized void removeListener(long userId, UpdateListener listener) {
- if (!listeners.containsKey(userId)) {
- listeners.put(userId, new HashSet<>());
- }
- listeners.get(userId).remove(listener);
- }
-
-}
diff --git a/src/main/java/org/traccar/database/DataManager.java b/src/main/java/org/traccar/database/DataManager.java
deleted file mode 100644
index 00c802fde..000000000
--- a/src/main/java/org/traccar/database/DataManager.java
+++ /dev/null
@@ -1,452 +0,0 @@
-/*
- * Copyright 2012 - 2021 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.database;
-
-import com.zaxxer.hikari.HikariConfig;
-import com.zaxxer.hikari.HikariDataSource;
-import liquibase.Contexts;
-import liquibase.Liquibase;
-import liquibase.database.Database;
-import liquibase.database.DatabaseFactory;
-import liquibase.exception.LiquibaseException;
-import liquibase.resource.FileSystemResourceAccessor;
-import liquibase.resource.ResourceAccessor;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.traccar.Context;
-import org.traccar.config.Config;
-import org.traccar.config.Keys;
-import org.traccar.model.Attribute;
-import org.traccar.model.BaseModel;
-import org.traccar.model.Calendar;
-import org.traccar.model.Command;
-import org.traccar.model.Device;
-import org.traccar.model.Driver;
-import org.traccar.model.Event;
-import org.traccar.model.Geofence;
-import org.traccar.model.Group;
-import org.traccar.model.Maintenance;
-import org.traccar.model.ManagedUser;
-import org.traccar.model.Notification;
-import org.traccar.model.Order;
-import org.traccar.model.Permission;
-import org.traccar.model.Position;
-import org.traccar.model.Server;
-import org.traccar.model.Statistics;
-import org.traccar.model.User;
-
-import javax.sql.DataSource;
-import java.beans.Introspector;
-import java.io.File;
-import java.lang.reflect.Method;
-import java.net.URL;
-import java.sql.SQLException;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.Set;
-
-public class DataManager {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(DataManager.class);
-
- public static final String ACTION_SELECT_ALL = "selectAll";
- public static final String ACTION_SELECT = "select";
- public static final String ACTION_INSERT = "insert";
- public static final String ACTION_UPDATE = "update";
- public static final String ACTION_DELETE = "delete";
-
- private final Config config;
-
- private DataSource dataSource;
-
- public DataSource getDataSource() {
- return dataSource;
- }
-
- private boolean generateQueries;
-
- private final boolean forceLdap;
-
- public DataManager(Config config) throws Exception {
- this.config = config;
-
- forceLdap = config.getBoolean(Keys.LDAP_FORCE);
-
- initDatabase();
- initDatabaseSchema();
- }
-
- private void initDatabase() throws Exception {
-
- String driverFile = config.getString(Keys.DATABASE_DRIVER_FILE);
- if (driverFile != null) {
- ClassLoader classLoader = ClassLoader.getSystemClassLoader();
- try {
- Method method = classLoader.getClass().getDeclaredMethod("addURL", URL.class);
- method.setAccessible(true);
- method.invoke(classLoader, new File(driverFile).toURI().toURL());
- } catch (NoSuchMethodException e) {
- Method method = classLoader.getClass()
- .getDeclaredMethod("appendToClassPathForInstrumentation", String.class);
- method.setAccessible(true);
- method.invoke(classLoader, driverFile);
- }
- }
-
- String driver = config.getString(Keys.DATABASE_DRIVER);
- if (driver != null) {
- Class.forName(driver);
- }
-
- HikariConfig hikariConfig = new HikariConfig();
- hikariConfig.setDriverClassName(driver);
- hikariConfig.setJdbcUrl(config.getString(Keys.DATABASE_URL));
- hikariConfig.setUsername(config.getString(Keys.DATABASE_USER));
- hikariConfig.setPassword(config.getString(Keys.DATABASE_PASSWORD));
- hikariConfig.setConnectionInitSql(config.getString(Keys.DATABASE_CHECK_CONNECTION));
- hikariConfig.setIdleTimeout(600000);
-
- int maxPoolSize = config.getInteger(Keys.DATABASE_MAX_POOL_SIZE);
- if (maxPoolSize != 0) {
- hikariConfig.setMaximumPoolSize(maxPoolSize);
- }
-
- generateQueries = config.getBoolean(Keys.DATABASE_GENERATE_QUERIES);
-
- dataSource = new HikariDataSource(hikariConfig);
- }
-
- public static String constructObjectQuery(String action, Class<?> clazz, boolean extended) {
- switch (action) {
- case ACTION_INSERT:
- case ACTION_UPDATE:
- StringBuilder result = new StringBuilder();
- StringBuilder fields = new StringBuilder();
- StringBuilder values = new StringBuilder();
-
- Set<Method> methods = new HashSet<>(Arrays.asList(clazz.getMethods()));
- methods.removeAll(Arrays.asList(Object.class.getMethods()));
- methods.removeAll(Arrays.asList(BaseModel.class.getMethods()));
- for (Method method : methods) {
- boolean skip;
- if (extended) {
- skip = !method.isAnnotationPresent(QueryExtended.class);
- } else {
- skip = method.isAnnotationPresent(QueryIgnore.class)
- || method.isAnnotationPresent(QueryExtended.class) && !action.equals(ACTION_INSERT);
- }
- if (!skip && method.getName().startsWith("get") && method.getParameterTypes().length == 0) {
- String name = Introspector.decapitalize(method.getName().substring(3));
- if (action.equals(ACTION_INSERT)) {
- fields.append(name).append(", ");
- values.append(":").append(name).append(", ");
- } else {
- fields.append(name).append(" = :").append(name).append(", ");
- }
- }
- }
- fields.setLength(fields.length() - 2);
- if (action.equals(ACTION_INSERT)) {
- values.setLength(values.length() - 2);
- result.append("INSERT INTO ").append(getObjectsTableName(clazz)).append(" (");
- result.append(fields).append(") ");
- result.append("VALUES (").append(values).append(")");
- } else {
- result.append("UPDATE ").append(getObjectsTableName(clazz)).append(" SET ");
- result.append(fields);
- result.append(" WHERE id = :id");
- }
- return result.toString();
- case ACTION_SELECT_ALL:
- return "SELECT * FROM " + getObjectsTableName(clazz);
- case ACTION_SELECT:
- return "SELECT * FROM " + getObjectsTableName(clazz) + " WHERE id = :id";
- case ACTION_DELETE:
- return "DELETE FROM " + getObjectsTableName(clazz) + " WHERE id = :id";
- default:
- throw new IllegalArgumentException("Unknown action");
- }
- }
-
- public static String constructPermissionQuery(String action, Class<?> owner, Class<?> property) {
- switch (action) {
- case ACTION_SELECT_ALL:
- return "SELECT " + makeNameId(owner) + ", " + makeNameId(property) + " FROM "
- + getPermissionsTableName(owner, property);
- case ACTION_INSERT:
- return "INSERT INTO " + getPermissionsTableName(owner, property)
- + " (" + makeNameId(owner) + ", " + makeNameId(property) + ") VALUES (:"
- + makeNameId(owner) + ", :" + makeNameId(property) + ")";
- case ACTION_DELETE:
- return "DELETE FROM " + getPermissionsTableName(owner, property)
- + " WHERE " + makeNameId(owner) + " = :" + makeNameId(owner)
- + " AND " + makeNameId(property) + " = :" + makeNameId(property);
- default:
- throw new IllegalArgumentException("Unknown action");
- }
- }
-
- private String getQuery(String key) {
- String query = config.getString(key);
- if (query == null) {
- LOGGER.info("Query not provided: " + key);
- }
- return query;
- }
-
- public String getQuery(String action, Class<?> clazz) {
- return getQuery(action, clazz, false);
- }
-
- public String getQuery(String action, Class<?> clazz, boolean extended) {
- String queryName;
- if (action.equals(ACTION_SELECT_ALL)) {
- queryName = "database.select" + clazz.getSimpleName() + "s";
- } else {
- queryName = "database." + action.toLowerCase() + clazz.getSimpleName();
- if (extended) {
- queryName += "Extended";
- }
- }
- String query = config.getString(queryName);
- if (query == null) {
- if (generateQueries) {
- query = constructObjectQuery(action, clazz, extended);
- } else {
- LOGGER.info("Query not provided: " + queryName);
- }
- }
- return query;
- }
-
- public String getQuery(String action, Class<?> owner, Class<?> property) {
- String queryName;
- switch (action) {
- case ACTION_SELECT_ALL:
- queryName = "database.select" + owner.getSimpleName() + property.getSimpleName() + "s";
- break;
- case ACTION_INSERT:
- queryName = "database.link" + owner.getSimpleName() + property.getSimpleName();
- break;
- default:
- queryName = "database.unlink" + owner.getSimpleName() + property.getSimpleName();
- break;
- }
- String query = config.getString(queryName);
- if (query == null) {
- if (generateQueries) {
- query = constructPermissionQuery(
- action, owner, property.equals(User.class) ? ManagedUser.class : property);
- } else {
- LOGGER.info("Query not provided: " + queryName);
- }
- }
- return query;
- }
-
- private static String getPermissionsTableName(Class<?> owner, Class<?> property) {
- String propertyName = property.getSimpleName();
- if (propertyName.equals("ManagedUser")) {
- propertyName = "User";
- }
- return "tc_" + Introspector.decapitalize(owner.getSimpleName())
- + "_" + Introspector.decapitalize(propertyName);
- }
-
- private static String getObjectsTableName(Class<?> clazz) {
- String result = "tc_" + Introspector.decapitalize(clazz.getSimpleName());
- // Add "s" ending if object name is not plural already
- if (!result.endsWith("s")) {
- result += "s";
- }
- return result;
- }
-
- private void initDatabaseSchema() throws LiquibaseException {
-
- if (config.hasKey(Keys.DATABASE_CHANGELOG)) {
-
- ResourceAccessor resourceAccessor = new FileSystemResourceAccessor(new File("."));
-
- Database database = DatabaseFactory.getInstance().openDatabase(
- config.getString(Keys.DATABASE_URL),
- config.getString(Keys.DATABASE_USER),
- config.getString(Keys.DATABASE_PASSWORD),
- config.getString(Keys.DATABASE_DRIVER),
- null, null, null, resourceAccessor);
-
- Liquibase liquibase = new Liquibase(
- config.getString(Keys.DATABASE_CHANGELOG), resourceAccessor, database);
-
- liquibase.clearCheckSums();
-
- liquibase.update(new Contexts());
- }
- }
-
- public User login(String email, String password) throws SQLException {
- User user = QueryBuilder.create(dataSource, getQuery("database.loginUser"))
- .setString("email", email.trim())
- .executeQuerySingle(User.class);
- LdapProvider ldapProvider = Context.getLdapProvider();
- if (user != null) {
- if (ldapProvider != null && user.getLogin() != null && ldapProvider.login(user.getLogin(), password)
- || !forceLdap && user.isPasswordValid(password)) {
- return user;
- }
- } else {
- if (ldapProvider != null && ldapProvider.login(email, password)) {
- user = ldapProvider.getUser(email);
- Context.getUsersManager().addItem(user);
- return user;
- }
- }
- return null;
- }
-
- public void updateDeviceStatus(Device device) throws SQLException {
- QueryBuilder.create(dataSource, getQuery(ACTION_UPDATE, Device.class, true))
- .setObject(device)
- .executeUpdate();
- }
-
- public Collection<Position> getPositions(long deviceId, Date from, Date to) throws SQLException {
- return QueryBuilder.create(dataSource, getQuery("database.selectPositions"))
- .setLong("deviceId", deviceId)
- .setDate("from", from)
- .setDate("to", to)
- .executeQuery(Position.class);
- }
-
- public void updateLatestPosition(Position position) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.updateLatestPosition"))
- .setDate("now", new Date())
- .setObject(position)
- .executeUpdate();
- }
-
- public Collection<Position> getLatestPositions() throws SQLException {
- return QueryBuilder.create(dataSource, getQuery("database.selectLatestPositions"))
- .executeQuery(Position.class);
- }
-
- public Server getServer() throws SQLException {
- return QueryBuilder.create(dataSource, getQuery(ACTION_SELECT_ALL, Server.class))
- .executeQuerySingle(Server.class);
- }
-
- public Collection<Event> getEvents(long deviceId, Date from, Date to) throws SQLException {
- return QueryBuilder.create(dataSource, getQuery("database.selectEvents"))
- .setLong("deviceId", deviceId)
- .setDate("from", from)
- .setDate("to", to)
- .executeQuery(Event.class);
- }
-
- public Collection<Statistics> getStatistics(Date from, Date to) throws SQLException {
- return QueryBuilder.create(dataSource, getQuery("database.selectStatistics"))
- .setDate("from", from)
- .setDate("to", to)
- .executeQuery(Statistics.class);
- }
-
- public static Class<?> getClassByName(String name) throws ClassNotFoundException {
- switch (name.toLowerCase().replace("id", "")) {
- case "device":
- return Device.class;
- case "group":
- return Group.class;
- case "user":
- return User.class;
- case "manageduser":
- return ManagedUser.class;
- case "geofence":
- return Geofence.class;
- case "driver":
- return Driver.class;
- case "attribute":
- return Attribute.class;
- case "calendar":
- return Calendar.class;
- case "command":
- return Command.class;
- case "maintenance":
- return Maintenance.class;
- case "notification":
- return Notification.class;
- case "order":
- return Order.class;
- default:
- throw new ClassNotFoundException();
- }
- }
-
- private static String makeNameId(Class<?> clazz) {
- String name = clazz.getSimpleName();
- return Introspector.decapitalize(name) + (!name.contains("Id") ? "Id" : "");
- }
-
- public Collection<Permission> getPermissions(Class<? extends BaseModel> owner, Class<? extends BaseModel> property)
- throws SQLException, ClassNotFoundException {
- return QueryBuilder.create(dataSource, getQuery(ACTION_SELECT_ALL, owner, property))
- .executePermissionsQuery();
- }
-
- public void linkObject(Class<?> owner, long ownerId, Class<?> property, long propertyId, boolean link)
- throws SQLException {
- QueryBuilder.create(dataSource, getQuery(link ? ACTION_INSERT : ACTION_DELETE, owner, property))
- .setLong(makeNameId(owner), ownerId)
- .setLong(makeNameId(property), propertyId)
- .executeUpdate();
- }
-
- public <T extends BaseModel> T getObject(Class<T> clazz, long entityId) throws SQLException {
- return QueryBuilder.create(dataSource, getQuery(ACTION_SELECT, clazz))
- .setLong("id", entityId)
- .executeQuerySingle(clazz);
- }
-
- public <T extends BaseModel> Collection<T> getObjects(Class<T> clazz) throws SQLException {
- return QueryBuilder.create(dataSource, getQuery(ACTION_SELECT_ALL, clazz))
- .executeQuery(clazz);
- }
-
- public void addObject(BaseModel entity) throws SQLException {
- entity.setId(QueryBuilder.create(dataSource, getQuery(ACTION_INSERT, entity.getClass()), true)
- .setObject(entity)
- .executeUpdate());
- }
-
- public void updateObject(BaseModel entity) throws SQLException {
- QueryBuilder.create(dataSource, getQuery(ACTION_UPDATE, entity.getClass()))
- .setObject(entity)
- .executeUpdate();
- if (entity instanceof User && ((User) entity).getHashedPassword() != null) {
- QueryBuilder.create(dataSource, getQuery(ACTION_UPDATE, User.class, true))
- .setObject(entity)
- .executeUpdate();
- }
- }
-
- public void removeObject(Class<? extends BaseModel> clazz, long entityId) throws SQLException {
- QueryBuilder.create(dataSource, getQuery(ACTION_DELETE, clazz))
- .setLong("id", entityId)
- .executeUpdate();
- }
-
-}
diff --git a/src/main/java/org/traccar/database/DeviceLookupService.java b/src/main/java/org/traccar/database/DeviceLookupService.java
new file mode 100644
index 000000000..583b2ae35
--- /dev/null
+++ b/src/main/java/org/traccar/database/DeviceLookupService.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.database;
+
+import io.netty.util.Timeout;
+import io.netty.util.Timer;
+import io.netty.util.TimerTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.model.Device;
+import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Request;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+
+@Singleton
+public class DeviceLookupService {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(DeviceLookupService.class);
+
+ private static final long INFO_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(60);
+ private static final long THROTTLE_MIN_MS = TimeUnit.MINUTES.toMillis(1);
+ private static final long THROTTLE_MAX_MS = TimeUnit.MINUTES.toMillis(30);
+
+ private final Storage storage;
+ private final Timer timer;
+
+ private final boolean throttlingEnabled;
+
+ private static class IdentifierInfo {
+ private long lastQuery;
+ private long delay;
+ private Timeout timeout;
+ }
+
+ private final class IdentifierTask implements TimerTask {
+ private final String uniqueId;
+
+ private IdentifierTask(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ @Override
+ public void run(Timeout timeout) {
+ LOGGER.debug("Device lookup expired {}", uniqueId);
+ synchronized (DeviceLookupService.this) {
+ identifierMap.remove(uniqueId);
+ }
+ }
+ }
+
+ private final Map<String, IdentifierInfo> identifierMap = new ConcurrentHashMap<>();
+
+ @Inject
+ public DeviceLookupService(Config config, Storage storage, Timer timer) {
+ this.storage = storage;
+ this.timer = timer;
+ throttlingEnabled = config.getBoolean(Keys.DATABASE_THROTTLE_UNKNOWN);
+ }
+
+ private synchronized boolean isThrottled(String uniqueId) {
+ if (throttlingEnabled) {
+ IdentifierInfo info = identifierMap.get(uniqueId);
+ return info != null && System.currentTimeMillis() < info.lastQuery + info.delay;
+ } else {
+ return false;
+ }
+ }
+
+ private synchronized void lookupSucceeded(String uniqueId) {
+ if (throttlingEnabled) {
+ IdentifierInfo info = identifierMap.remove(uniqueId);
+ if (info != null) {
+ info.timeout.cancel();
+ }
+ }
+ }
+
+ private synchronized void lookupFailed(String uniqueId) {
+ if (throttlingEnabled) {
+ IdentifierInfo info = identifierMap.get(uniqueId);
+ if (info != null) {
+ info.timeout.cancel();
+ info.delay = Math.min(info.delay * 2, THROTTLE_MAX_MS);
+ } else {
+ info = new IdentifierInfo();
+ identifierMap.put(uniqueId, info);
+ info.delay = THROTTLE_MIN_MS;
+ }
+ info.lastQuery = System.currentTimeMillis();
+ info.timeout = timer.newTimeout(new IdentifierTask(uniqueId), INFO_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ LOGGER.debug("Device lookup {} throttled for {} ms", uniqueId, info.delay);
+ }
+ }
+
+ public Device lookup(String[] uniqueIds) {
+ Device device = null;
+ try {
+ for (String uniqueId : uniqueIds) {
+ if (!isThrottled(uniqueId)) {
+ device = storage.getObject(Device.class, new Request(
+ new Columns.All(), new Condition.Equals("uniqueId", uniqueId)));
+ if (device != null) {
+ lookupSucceeded(uniqueId);
+ break;
+ } else {
+ lookupFailed(uniqueId);
+ }
+ } else {
+ LOGGER.debug("Device lookup throttled {}", uniqueId);
+ }
+ }
+ } catch (StorageException e) {
+ LOGGER.warn("Find device error", e);
+ }
+ return device;
+ }
+
+}
diff --git a/src/main/java/org/traccar/database/DeviceManager.java b/src/main/java/org/traccar/database/DeviceManager.java
deleted file mode 100644
index c8a99274c..000000000
--- a/src/main/java/org/traccar/database/DeviceManager.java
+++ /dev/null
@@ -1,483 +0,0 @@
-/*
- * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.database;
-
-import java.sql.SQLException;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicLong;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.traccar.config.Config;
-import org.traccar.Context;
-import org.traccar.config.Keys;
-import org.traccar.model.Command;
-import org.traccar.model.Device;
-import org.traccar.model.DeviceState;
-import org.traccar.model.DeviceAccumulators;
-import org.traccar.model.Group;
-import org.traccar.model.Position;
-import org.traccar.model.Server;
-
-public class DeviceManager extends BaseObjectManager<Device> implements IdentityManager, ManagableObjects {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(DeviceManager.class);
-
- private final Config config;
- private final long dataRefreshDelay;
-
- private Map<String, Device> devicesByUniqueId;
- private Map<String, Device> devicesByPhone;
- private final AtomicLong devicesLastUpdate = new AtomicLong();
-
- private final Map<Long, Position> positions = new ConcurrentHashMap<>();
-
- private final Map<Long, DeviceState> deviceStates = new ConcurrentHashMap<>();
-
- public DeviceManager(DataManager dataManager) {
- super(dataManager, Device.class);
- this.config = Context.getConfig();
- try {
- writeLock();
- if (devicesByPhone == null) {
- devicesByPhone = new ConcurrentHashMap<>();
- }
- if (devicesByUniqueId == null) {
- devicesByUniqueId = new ConcurrentHashMap<>();
- }
- } finally {
- writeUnlock();
- }
- dataRefreshDelay = config.getLong(Keys.DATABASE_REFRESH_DELAY) * 1000;
- refreshLastPositions();
- }
-
- @Override
- public long addUnknownDevice(String uniqueId) {
- Device device = new Device();
- device.setName(uniqueId);
- device.setUniqueId(uniqueId);
- device.setCategory(Context.getConfig().getString(Keys.DATABASE_REGISTER_UNKNOWN_DEFAULT_CATEGORY));
-
- long defaultGroupId = Context.getConfig().getLong(Keys.DATABASE_REGISTER_UNKNOWN_DEFAULT_GROUP_ID);
- if (defaultGroupId != 0) {
- device.setGroupId(defaultGroupId);
- }
-
- try {
- addItem(device);
-
- LOGGER.info("Automatically registered device " + uniqueId);
-
- if (defaultGroupId != 0) {
- Context.getPermissionsManager().refreshDeviceAndGroupPermissions();
- Context.getPermissionsManager().refreshAllExtendedPermissions();
- }
-
- return device.getId();
- } catch (SQLException e) {
- LOGGER.warn("Automatic device registration error", e);
- return 0;
- }
- }
-
- public void updateDeviceCache(boolean force) throws SQLException {
- long lastUpdate = devicesLastUpdate.get();
- if ((force || System.currentTimeMillis() - lastUpdate > dataRefreshDelay)
- && devicesLastUpdate.compareAndSet(lastUpdate, System.currentTimeMillis())) {
- refreshItems();
- }
- }
-
- @Override
- public Device getByUniqueId(String uniqueId) throws SQLException {
- boolean forceUpdate;
- try {
- readLock();
- forceUpdate = !devicesByUniqueId.containsKey(uniqueId) && !config.getBoolean(Keys.DATABASE_IGNORE_UNKNOWN);
- } finally {
- readUnlock();
- }
- updateDeviceCache(forceUpdate);
- try {
- readLock();
- return devicesByUniqueId.get(uniqueId);
- } finally {
- readUnlock();
- }
- }
-
- @Override
- public String getDevicePassword(long id, String protocol, String defaultPassword) {
-
- String password = lookupAttributeString(id, Command.KEY_DEVICE_PASSWORD, null, false, false);
- if (password != null) {
- return password;
- }
-
- if (protocol != null) {
- password = Context.getConfig().getString(Keys.PROTOCOL_DEVICE_PASSWORD.withPrefix(protocol));
- if (password != null) {
- return password;
- }
- }
-
- return defaultPassword;
- }
-
- public Device getDeviceByPhone(String phone) {
- try {
- readLock();
- return devicesByPhone.get(phone);
- } finally {
- readUnlock();
- }
- }
-
- @Override
- public Set<Long> getAllItems() {
- Set<Long> result = super.getAllItems();
- if (result.isEmpty()) {
- try {
- updateDeviceCache(true);
- } catch (SQLException e) {
- LOGGER.warn("Update device cache error", e);
- }
- result = super.getAllItems();
- }
- return result;
- }
-
- public Collection<Device> getAllDevices() {
- return getItems(getAllItems());
- }
-
- public Set<Long> getAllUserItems(long userId) {
- return Context.getPermissionsManager().getDevicePermissions(userId);
- }
-
- @Override
- public Set<Long> getUserItems(long userId) {
- if (Context.getPermissionsManager() != null) {
- Set<Long> result = new HashSet<>();
- for (long deviceId : Context.getPermissionsManager().getDevicePermissions(userId)) {
- Device device = getById(deviceId);
- if (device != null && !device.getDisabled()) {
- result.add(deviceId);
- }
- }
- return result;
- } else {
- return new HashSet<>();
- }
- }
-
- public Set<Long> getAllManagedItems(long userId) {
- Set<Long> result = new HashSet<>(getAllUserItems(userId));
- for (long managedUserId : Context.getUsersManager().getUserItems(userId)) {
- result.addAll(getAllUserItems(managedUserId));
- }
- return result;
- }
-
- @Override
- public Set<Long> getManagedItems(long userId) {
- Set<Long> result = new HashSet<>(getUserItems(userId));
- for (long managedUserId : Context.getUsersManager().getUserItems(userId)) {
- result.addAll(getUserItems(managedUserId));
- }
- return result;
- }
-
- private void addByUniqueId(Device device) {
- try {
- writeLock();
- if (devicesByUniqueId == null) {
- devicesByUniqueId = new ConcurrentHashMap<>();
- }
- devicesByUniqueId.put(device.getUniqueId(), device);
- } finally {
- writeUnlock();
- }
- }
-
- private void removeByUniqueId(String deviceUniqueId) {
- try {
- writeLock();
- if (devicesByUniqueId != null) {
- devicesByUniqueId.remove(deviceUniqueId);
- }
- } finally {
- writeUnlock();
- }
- }
-
- private void addByPhone(Device device) {
- try {
- writeLock();
- if (devicesByPhone == null) {
- devicesByPhone = new ConcurrentHashMap<>();
- }
- devicesByPhone.put(device.getPhone(), device);
- } finally {
- writeUnlock();
- }
- }
-
- private void removeByPhone(String phone) {
- if (phone == null || phone.isEmpty()) {
- return;
- }
- try {
- writeLock();
- if (devicesByPhone != null) {
- devicesByPhone.remove(phone);
- }
- } finally {
- writeUnlock();
- }
- }
-
- @Override
- protected void addNewItem(Device device) {
- super.addNewItem(device);
- addByUniqueId(device);
- if (device.getPhone() != null && !device.getPhone().isEmpty()) {
- addByPhone(device);
- }
- if (Context.getGeofenceManager() != null) {
- Position lastPosition = getLastPosition(device.getId());
- if (lastPosition != null) {
- device.setGeofenceIds(Context.getGeofenceManager().getCurrentDeviceGeofences(lastPosition));
- }
- }
- }
-
- @Override
- protected void updateCachedItem(Device device) {
- Device cachedDevice = getById(device.getId());
- cachedDevice.setName(device.getName());
- cachedDevice.setGroupId(device.getGroupId());
- cachedDevice.setCategory(device.getCategory());
- cachedDevice.setContact(device.getContact());
- cachedDevice.setModel(device.getModel());
- cachedDevice.setDisabled(device.getDisabled());
- cachedDevice.setAttributes(device.getAttributes());
- if (!device.getUniqueId().equals(cachedDevice.getUniqueId())) {
- removeByUniqueId(cachedDevice.getUniqueId());
- cachedDevice.setUniqueId(device.getUniqueId());
- addByUniqueId(cachedDevice);
- }
- if (device.getPhone() != null && !device.getPhone().isEmpty()
- && !device.getPhone().equals(cachedDevice.getPhone())) {
- String phone = cachedDevice.getPhone();
- removeByPhone(phone);
- cachedDevice.setPhone(device.getPhone());
- addByPhone(cachedDevice);
- }
- }
-
- @Override
- protected void removeCachedItem(long deviceId) {
- Device cachedDevice = getById(deviceId);
- if (cachedDevice != null) {
- String deviceUniqueId = cachedDevice.getUniqueId();
- String phone = cachedDevice.getPhone();
- super.removeCachedItem(deviceId);
- removeByUniqueId(deviceUniqueId);
- removeByPhone(phone);
- }
- positions.remove(deviceId);
- }
-
- public void updateDeviceStatus(Device device) throws SQLException {
- getDataManager().updateDeviceStatus(device);
- Device cachedDevice = getById(device.getId());
- if (cachedDevice != null) {
- cachedDevice.setStatus(device.getStatus());
- }
- }
-
- private void refreshLastPositions() {
- if (getDataManager() != null) {
- try {
- for (Position position : getDataManager().getLatestPositions()) {
- positions.put(position.getDeviceId(), position);
- }
- } catch (SQLException error) {
- LOGGER.warn("Load latest positions error", error);
- }
- }
- }
-
- public boolean isLatestPosition(Position position) {
- Position lastPosition = getLastPosition(position.getDeviceId());
- return lastPosition == null || position.getFixTime().compareTo(lastPosition.getFixTime()) >= 0;
- }
-
- public void updateLatestPosition(Position position) throws SQLException {
-
- if (isLatestPosition(position)) {
-
- getDataManager().updateLatestPosition(position);
-
- Device device = getById(position.getDeviceId());
- if (device != null) {
- device.setPositionId(position.getId());
- }
-
- positions.put(position.getDeviceId(), position);
-
- if (Context.getConnectionManager() != null) {
- Context.getConnectionManager().updatePosition(position);
- }
- }
- }
-
- @Override
- public Position getLastPosition(long deviceId) {
- return positions.get(deviceId);
- }
-
- public Collection<Position> getInitialState(long userId) {
-
- List<Position> result = new LinkedList<>();
-
- if (Context.getPermissionsManager() != null) {
- for (long deviceId : Context.getPermissionsManager().getUserAdmin(userId)
- ? getAllUserItems(userId) : getUserItems(userId)) {
- if (positions.containsKey(deviceId)) {
- result.add(positions.get(deviceId));
- }
- }
- }
-
- return result;
- }
-
- @Override
- public boolean lookupAttributeBoolean(
- long deviceId, String attributeName, boolean defaultValue, boolean lookupServer, boolean lookupConfig) {
- Object result = lookupAttribute(deviceId, attributeName, lookupServer, lookupConfig);
- if (result != null) {
- return result instanceof String ? Boolean.parseBoolean((String) result) : (Boolean) result;
- }
- return defaultValue;
- }
-
- @Override
- public String lookupAttributeString(
- long deviceId, String attributeName, String defaultValue, boolean lookupServer, boolean lookupConfig) {
- Object result = lookupAttribute(deviceId, attributeName, lookupServer, lookupConfig);
- return result != null ? (String) result : defaultValue;
- }
-
- @Override
- public int lookupAttributeInteger(
- long deviceId, String attributeName, int defaultValue, boolean lookupServer, boolean lookupConfig) {
- Object result = lookupAttribute(deviceId, attributeName, lookupServer, lookupConfig);
- if (result != null) {
- return result instanceof String ? Integer.parseInt((String) result) : ((Number) result).intValue();
- }
- return defaultValue;
- }
-
- @Override
- public long lookupAttributeLong(
- long deviceId, String attributeName, long defaultValue, boolean lookupServer, boolean lookupConfig) {
- Object result = lookupAttribute(deviceId, attributeName, lookupServer, lookupConfig);
- if (result != null) {
- return result instanceof String ? Long.parseLong((String) result) : ((Number) result).longValue();
- }
- return defaultValue;
- }
-
- public double lookupAttributeDouble(
- long deviceId, String attributeName, double defaultValue, boolean lookupServer, boolean lookupConfig) {
- Object result = lookupAttribute(deviceId, attributeName, lookupServer, lookupConfig);
- if (result != null) {
- return result instanceof String ? Double.parseDouble((String) result) : ((Number) result).doubleValue();
- }
- return defaultValue;
- }
-
- private Object lookupAttribute(long deviceId, String attributeName, boolean lookupServer, boolean lookupConfig) {
- Object result = null;
- Device device = getById(deviceId);
- if (device != null) {
- result = device.getAttributes().get(attributeName);
- if (result == null) {
- long groupId = device.getGroupId();
- while (groupId != 0) {
- Group group = Context.getGroupsManager().getById(groupId);
- if (group != null) {
- result = group.getAttributes().get(attributeName);
- if (result != null) {
- break;
- }
- groupId = group.getGroupId();
- } else {
- groupId = 0;
- }
- }
- }
- if (result == null && lookupServer) {
- Server server = Context.getPermissionsManager().getServer();
- result = server.getAttributes().get(attributeName);
- }
- if (result == null && lookupConfig) {
- result = Context.getConfig().getString(attributeName);
- }
- }
- return result;
- }
-
- public void resetDeviceAccumulators(DeviceAccumulators deviceAccumulators) throws SQLException {
- Position last = positions.get(deviceAccumulators.getDeviceId());
- if (last != null) {
- if (deviceAccumulators.getTotalDistance() != null) {
- last.getAttributes().put(Position.KEY_TOTAL_DISTANCE, deviceAccumulators.getTotalDistance());
- }
- if (deviceAccumulators.getHours() != null) {
- last.getAttributes().put(Position.KEY_HOURS, deviceAccumulators.getHours());
- }
- getDataManager().addObject(last);
- updateLatestPosition(last);
- } else {
- throw new IllegalArgumentException();
- }
- }
-
- public DeviceState getDeviceState(long deviceId) {
- DeviceState deviceState = deviceStates.get(deviceId);
- if (deviceState == null) {
- deviceState = new DeviceState();
- deviceStates.put(deviceId, deviceState);
- }
- return deviceState;
- }
-
- public void setDeviceState(long deviceId, DeviceState deviceState) {
- deviceStates.put(deviceId, deviceState);
- }
-
-}
diff --git a/src/main/java/org/traccar/database/DriversManager.java b/src/main/java/org/traccar/database/DriversManager.java
deleted file mode 100644
index d111cd643..000000000
--- a/src/main/java/org/traccar/database/DriversManager.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
- * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.database;
-
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.traccar.model.Driver;
-
-public class DriversManager extends ExtendedObjectManager<Driver> {
-
- private Map<String, Driver> driversByUniqueId;
-
- public DriversManager(DataManager dataManager) {
- super(dataManager, Driver.class);
- try {
- writeLock();
- if (driversByUniqueId == null) {
- driversByUniqueId = new ConcurrentHashMap<>();
- }
- } finally {
- writeUnlock();
- }
- }
-
- private void addByUniqueId(Driver driver) {
- try {
- writeLock();
- if (driversByUniqueId == null) {
- driversByUniqueId = new ConcurrentHashMap<>();
- }
- driversByUniqueId.put(driver.getUniqueId(), driver);
- } finally {
- writeUnlock();
- }
- }
-
- private void removeByUniqueId(String driverUniqueId) {
- try {
- writeLock();
- if (driversByUniqueId == null) {
- driversByUniqueId = new ConcurrentHashMap<>();
- }
- driversByUniqueId.remove(driverUniqueId);
- } finally {
- writeUnlock();
- }
- }
-
- @Override
- protected void addNewItem(Driver driver) {
- super.addNewItem(driver);
- addByUniqueId(driver);
- }
-
- @Override
- protected void updateCachedItem(Driver driver) {
- Driver cachedDriver = getById(driver.getId());
- cachedDriver.setName(driver.getName());
- if (!driver.getUniqueId().equals(cachedDriver.getUniqueId())) {
- removeByUniqueId(cachedDriver.getUniqueId());
- cachedDriver.setUniqueId(driver.getUniqueId());
- addByUniqueId(cachedDriver);
- }
- cachedDriver.setAttributes(driver.getAttributes());
- }
-
- @Override
- protected void removeCachedItem(long driverId) {
- Driver cachedDriver = getById(driverId);
- if (cachedDriver != null) {
- String driverUniqueId = cachedDriver.getUniqueId();
- super.removeCachedItem(driverId);
- removeByUniqueId(driverUniqueId);
- }
- }
-
- public Driver getDriverByUniqueId(String uniqueId) {
- try {
- readLock();
- return driversByUniqueId.get(uniqueId);
- } finally {
- readUnlock();
- }
- }
-}
diff --git a/src/main/java/org/traccar/database/ExtendedObjectManager.java b/src/main/java/org/traccar/database/ExtendedObjectManager.java
deleted file mode 100644
index 93e5820fb..000000000
--- a/src/main/java/org/traccar/database/ExtendedObjectManager.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
- * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.database;
-
-import java.sql.SQLException;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.traccar.Context;
-import org.traccar.model.Device;
-import org.traccar.model.Group;
-import org.traccar.model.Permission;
-import org.traccar.model.BaseModel;
-
-public abstract class ExtendedObjectManager<T extends BaseModel> extends SimpleObjectManager<T> {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(ExtendedObjectManager.class);
-
- private final Map<Long, Set<Long>> deviceItems = new ConcurrentHashMap<>();
- private final Map<Long, Set<Long>> deviceItemsWithGroups = new ConcurrentHashMap<>();
- private final Map<Long, Set<Long>> groupItems = new ConcurrentHashMap<>();
-
- protected ExtendedObjectManager(DataManager dataManager, Class<T> baseClass) {
- super(dataManager, baseClass);
- refreshExtendedPermissions();
- }
-
- public final Set<Long> getGroupItems(long groupId) {
- try {
- readLock();
- Set<Long> result = groupItems.get(groupId);
- if (result != null) {
- return new HashSet<>(result);
- } else {
- return new HashSet<>();
- }
- } finally {
- readUnlock();
- }
- }
-
- public final Set<Long> getDeviceItems(long deviceId) {
- try {
- readLock();
- Set<Long> result = deviceItems.get(deviceId);
- if (result != null) {
- return new HashSet<>(result);
- } else {
- return new HashSet<>();
- }
- } finally {
- readUnlock();
- }
- }
-
- public Set<Long> getAllDeviceItems(long deviceId) {
- try {
- readLock();
- Set<Long> result = deviceItemsWithGroups.get(deviceId);
- if (result != null) {
- return new HashSet<>(result);
- } else {
- return new HashSet<>();
- }
- } finally {
- readUnlock();
- }
- }
-
- @Override
- public void removeItem(long itemId) throws SQLException {
- super.removeItem(itemId);
- refreshExtendedPermissions();
- }
-
- public void refreshExtendedPermissions() {
- if (getDataManager() != null) {
- try {
- Collection<Permission> databaseGroupPermissions =
- getDataManager().getPermissions(Group.class, getBaseClass());
-
- Collection<Permission> databaseDevicePermissions =
- getDataManager().getPermissions(Device.class, getBaseClass());
-
- writeLock();
-
- groupItems.clear();
- deviceItems.clear();
- deviceItemsWithGroups.clear();
-
- for (Permission groupPermission : databaseGroupPermissions) {
- groupItems
- .computeIfAbsent(groupPermission.getOwnerId(), key -> new HashSet<>())
- .add(groupPermission.getPropertyId());
- }
-
- for (Permission devicePermission : databaseDevicePermissions) {
- deviceItems
- .computeIfAbsent(devicePermission.getOwnerId(), key -> new HashSet<>())
- .add(devicePermission.getPropertyId());
- deviceItemsWithGroups
- .computeIfAbsent(devicePermission.getOwnerId(), key -> new HashSet<>())
- .add(devicePermission.getPropertyId());
- }
-
- for (Device device : Context.getDeviceManager().getAllDevices()) {
- long groupId = device.getGroupId();
- while (groupId > 0) {
- deviceItemsWithGroups
- .computeIfAbsent(device.getId(), key -> new HashSet<>())
- .addAll(groupItems.getOrDefault(groupId, new HashSet<>()));
- Group group = Context.getGroupsManager().getById(groupId);
- groupId = group != null ? group.getGroupId() : 0;
- }
- }
-
- } catch (SQLException | ClassNotFoundException error) {
- LOGGER.warn("Refresh permissions error", error);
- } finally {
- writeUnlock();
- }
- }
- }
-}
diff --git a/src/main/java/org/traccar/database/GeofenceManager.java b/src/main/java/org/traccar/database/GeofenceManager.java
deleted file mode 100644
index a32847cf9..000000000
--- a/src/main/java/org/traccar/database/GeofenceManager.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.database;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.traccar.Context;
-import org.traccar.model.Device;
-import org.traccar.model.Geofence;
-import org.traccar.model.Position;
-
-public class GeofenceManager extends ExtendedObjectManager<Geofence> {
-
- public GeofenceManager(DataManager dataManager) {
- super(dataManager, Geofence.class);
- }
-
- @Override
- public final void refreshExtendedPermissions() {
- super.refreshExtendedPermissions();
- recalculateDevicesGeofences();
- }
-
- public List<Long> getCurrentDeviceGeofences(Position position) {
- List<Long> result = new ArrayList<>();
- for (long geofenceId : getAllDeviceItems(position.getDeviceId())) {
- Geofence geofence = getById(geofenceId);
- if (geofence != null && geofence.getGeometry()
- .containsPoint(position.getLatitude(), position.getLongitude())) {
- result.add(geofenceId);
- }
- }
- return result;
- }
-
- public void recalculateDevicesGeofences() {
- for (Device device : Context.getDeviceManager().getAllDevices()) {
- List<Long> deviceGeofenceIds = device.getGeofenceIds();
- if (deviceGeofenceIds == null) {
- deviceGeofenceIds = new ArrayList<>();
- } else {
- deviceGeofenceIds.clear();
- }
- Position lastPosition = Context.getIdentityManager().getLastPosition(device.getId());
- if (lastPosition != null && getAllDeviceItems(device.getId()) != null) {
- deviceGeofenceIds.addAll(getCurrentDeviceGeofences(lastPosition));
- }
- device.setGeofenceIds(deviceGeofenceIds);
- }
- }
-
-}
diff --git a/src/main/java/org/traccar/database/GroupsManager.java b/src/main/java/org/traccar/database/GroupsManager.java
deleted file mode 100644
index c35f35f93..000000000
--- a/src/main/java/org/traccar/database/GroupsManager.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
- * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.database;
-
-import java.sql.SQLException;
-import java.util.HashSet;
-import java.util.Set;
-
-import org.traccar.Context;
-import org.traccar.model.Group;
-
-public class GroupsManager extends BaseObjectManager<Group> implements ManagableObjects {
-
- public GroupsManager(DataManager dataManager) {
- super(dataManager, Group.class);
- }
-
- private void checkGroupCycles(Group group) {
- Set<Long> groups = new HashSet<>();
- while (group != null) {
- if (groups.contains(group.getId())) {
- throw new IllegalArgumentException("Cycle in group hierarchy");
- }
- groups.add(group.getId());
- group = getById(group.getGroupId());
- }
- }
-
- @Override
- public Set<Long> getAllItems() {
- Set<Long> result = super.getAllItems();
- if (result.isEmpty()) {
- refreshItems();
- result = super.getAllItems();
- }
- return result;
- }
-
- @Override
- protected void addNewItem(Group group) {
- checkGroupCycles(group);
- super.addNewItem(group);
- }
-
- @Override
- public void updateItem(Group group) throws SQLException {
- checkGroupCycles(group);
- super.updateItem(group);
- }
-
- @Override
- public Set<Long> getUserItems(long userId) {
- if (Context.getPermissionsManager() != null) {
- return Context.getPermissionsManager().getGroupPermissions(userId);
- } else {
- return new HashSet<>();
- }
- }
-
- @Override
- public Set<Long> getManagedItems(long userId) {
- Set<Long> result = getUserItems(userId);
- for (long managedUserId : Context.getUsersManager().getUserItems(userId)) {
- result.addAll(getUserItems(managedUserId));
- }
- return result;
- }
-
-}
diff --git a/src/main/java/org/traccar/database/IdentityManager.java b/src/main/java/org/traccar/database/IdentityManager.java
deleted file mode 100644
index af6a6ce71..000000000
--- a/src/main/java/org/traccar/database/IdentityManager.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.database;
-
-import org.traccar.model.Device;
-import org.traccar.model.Position;
-
-public interface IdentityManager {
-
- long addUnknownDevice(String uniqueId);
-
- Device getById(long id);
-
- Device getByUniqueId(String uniqueId) throws Exception;
-
- String getDevicePassword(long id, String protocol, String defaultPassword);
-
- Position getLastPosition(long deviceId);
-
- boolean isLatestPosition(Position position);
-
- boolean lookupAttributeBoolean(
- long deviceId, String attributeName, boolean defaultValue, boolean lookupServer, boolean lookupConfig);
-
- String lookupAttributeString(
- long deviceId, String attributeName, String defaultValue, boolean lookupServer, boolean lookupConfig);
-
- int lookupAttributeInteger(
- long deviceId, String attributeName, int defaultValue, boolean lookupServer, boolean lookupConfig);
-
- long lookupAttributeLong(
- long deviceId, String attributeName, long defaultValue, boolean lookupServer, boolean lookupConfig);
-
- double lookupAttributeDouble(
- long deviceId, String attributeName, double defaultValue, boolean lookupServer, boolean lookupConfig);
-
-}
diff --git a/src/main/java/org/traccar/database/LdapProvider.java b/src/main/java/org/traccar/database/LdapProvider.java
index d659a11a1..d517294b8 100644
--- a/src/main/java/org/traccar/database/LdapProvider.java
+++ b/src/main/java/org/traccar/database/LdapProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/src/main/java/org/traccar/database/MailManager.java b/src/main/java/org/traccar/database/MailManager.java
deleted file mode 100644
index d94f55cda..000000000
--- a/src/main/java/org/traccar/database/MailManager.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright 2016 - 2021 Anton Tananaev (anton@traccar.org)
- * Copyright 2017 - 2018 Andrey Kunitsyn (andrey@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.database;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.traccar.Context;
-import org.traccar.Main;
-import org.traccar.model.User;
-import org.traccar.notification.PropertiesProvider;
-
-import javax.mail.BodyPart;
-import javax.mail.Message;
-import javax.mail.MessagingException;
-import javax.mail.Multipart;
-import javax.mail.Session;
-import javax.mail.Transport;
-import javax.mail.internet.InternetAddress;
-import javax.mail.internet.MimeBodyPart;
-import javax.mail.internet.MimeMessage;
-import javax.mail.internet.MimeMultipart;
-import java.util.Date;
-import java.util.Properties;
-
-public final class MailManager {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(MailManager.class);
-
- private static Properties getProperties(PropertiesProvider provider) {
- Properties properties = new Properties();
- String host = provider.getString("mail.smtp.host");
- if (host != null) {
- properties.put("mail.transport.protocol", provider.getString("mail.transport.protocol", "smtp"));
- properties.put("mail.smtp.host", host);
- properties.put("mail.smtp.port", String.valueOf(provider.getInteger("mail.smtp.port", 25)));
-
- Boolean starttlsEnable = provider.getBoolean("mail.smtp.starttls.enable");
- if (starttlsEnable != null) {
- properties.put("mail.smtp.starttls.enable", String.valueOf(starttlsEnable));
- }
- Boolean starttlsRequired = provider.getBoolean("mail.smtp.starttls.required");
- if (starttlsRequired != null) {
- properties.put("mail.smtp.starttls.required", String.valueOf(starttlsRequired));
- }
-
- Boolean sslEnable = provider.getBoolean("mail.smtp.ssl.enable");
- if (sslEnable != null) {
- properties.put("mail.smtp.ssl.enable", String.valueOf(sslEnable));
- }
- String sslTrust = provider.getString("mail.smtp.ssl.trust");
- if (sslTrust != null) {
- properties.put("mail.smtp.ssl.trust", sslTrust);
- }
-
- String sslProtocols = provider.getString("mail.smtp.ssl.protocols");
- if (sslProtocols != null) {
- properties.put("mail.smtp.ssl.protocols", sslProtocols);
- }
-
- String username = provider.getString("mail.smtp.username");
- if (username != null) {
- properties.put("mail.smtp.username", username);
- }
- String password = provider.getString("mail.smtp.password");
- if (password != null) {
- properties.put("mail.smtp.password", password);
- }
- String from = provider.getString("mail.smtp.from");
- if (from != null) {
- properties.put("mail.smtp.from", from);
- }
- }
- return properties;
- }
-
- public boolean getEmailEnabled() {
- return Context.getConfig().hasKey("mail.smtp.host");
- }
-
- public void sendMessage(
- long userId, String subject, String body) throws MessagingException {
- sendMessage(userId, subject, body, null);
- }
-
- public void sendMessage(
- long userId, String subject, String body, MimeBodyPart attachment) throws MessagingException {
- User user = Context.getPermissionsManager().getUser(userId);
-
- Properties properties = null;
- if (!Context.getConfig().getBoolean("mail.smtp.ignoreUserConfig")) {
- properties = getProperties(new PropertiesProvider(user));
- }
- if (properties == null || !properties.containsKey("mail.smtp.host")) {
- properties = getProperties(new PropertiesProvider(Context.getConfig()));
- }
- if (!properties.containsKey("mail.smtp.host")) {
- LOGGER.warn("No SMTP configuration found");
- return;
- }
-
- Session session = Session.getInstance(properties);
-
- MimeMessage message = new MimeMessage(session);
-
- String from = properties.getProperty("mail.smtp.from");
- if (from != null) {
- message.setFrom(new InternetAddress(from));
- }
-
- message.addRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail()));
- message.setSubject(subject);
- message.setSentDate(new Date());
-
- if (attachment != null) {
- Multipart multipart = new MimeMultipart();
-
- BodyPart messageBodyPart = new MimeBodyPart();
- messageBodyPart.setContent(body, "text/html; charset=utf-8");
- multipart.addBodyPart(messageBodyPart);
- multipart.addBodyPart(attachment);
-
- message.setContent(multipart);
- } else {
- message.setContent(body, "text/html; charset=utf-8");
- }
-
- try (Transport transport = session.getTransport()) {
- Main.getInjector().getInstance(StatisticsManager.class).registerMail();
- transport.connect(
- properties.getProperty("mail.smtp.host"),
- properties.getProperty("mail.smtp.username"),
- properties.getProperty("mail.smtp.password"));
- transport.sendMessage(message, message.getAllRecipients());
- }
- }
-
-}
diff --git a/src/main/java/org/traccar/database/MediaManager.java b/src/main/java/org/traccar/database/MediaManager.java
index edade5766..c1ef810ee 100644
--- a/src/main/java/org/traccar/database/MediaManager.java
+++ b/src/main/java/org/traccar/database/MediaManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,10 +18,15 @@ package org.traccar.database;
import io.netty.buffer.ByteBuf;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import javax.inject.Inject;
+import javax.inject.Singleton;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
@@ -30,14 +35,16 @@ import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Date;
+@Singleton
public class MediaManager {
private static final Logger LOGGER = LoggerFactory.getLogger(MediaManager.class);
- private String path;
+ private final String path;
- public MediaManager(String path) {
- this.path = path;
+ @Inject
+ public MediaManager(Config config) {
+ this.path = config.getString(Keys.MEDIA_PATH);
}
private File createFile(String uniqueId, String name) throws IOException {
@@ -49,6 +56,10 @@ public class MediaManager {
return filePath.toFile();
}
+ public OutputStream createFileStream(String uniqueId, String name, String extension) throws IOException {
+ return new FileOutputStream(createFile(uniqueId, name + "." + extension));
+ }
+
public String writeFile(String uniqueId, ByteBuf buf, String extension) {
if (path != null) {
int size = buf.readableBytes();
diff --git a/src/main/java/org/traccar/database/NotificationManager.java b/src/main/java/org/traccar/database/NotificationManager.java
index 9f9a83cd2..cb971b082 100644
--- a/src/main/java/org/traccar/database/NotificationManager.java
+++ b/src/main/java/org/traccar/database/NotificationManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2016 - 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,128 +16,141 @@
*/
package org.traccar.database;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.sql.SQLException;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.traccar.Context;
+import org.traccar.config.Config;
import org.traccar.config.Keys;
+import org.traccar.forward.EventData;
+import org.traccar.forward.EventForwarder;
+import org.traccar.geocoder.Geocoder;
import org.traccar.model.Calendar;
+import org.traccar.model.Device;
import org.traccar.model.Event;
+import org.traccar.model.Geofence;
+import org.traccar.model.Maintenance;
import org.traccar.model.Notification;
import org.traccar.model.Position;
-import org.traccar.model.Typed;
+import org.traccar.notification.MessageException;
+import org.traccar.notification.NotificatorManager;
+import org.traccar.session.cache.CacheManager;
+import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Request;
+
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
-public class NotificationManager extends ExtendedObjectManager<Notification> {
+@Singleton
+public class NotificationManager {
private static final Logger LOGGER = LoggerFactory.getLogger(NotificationManager.class);
- private final boolean geocodeOnRequest;
+ private final Storage storage;
+ private final CacheManager cacheManager;
+ private final EventForwarder eventForwarder;
+ private final NotificatorManager notificatorManager;
+ private final Geocoder geocoder;
- public NotificationManager(DataManager dataManager) {
- super(dataManager, Notification.class);
- geocodeOnRequest = Context.getConfig().getBoolean(Keys.GEOCODER_ON_REQUEST);
- }
+ private final boolean geocodeOnRequest;
- private Set<Long> getEffectiveNotifications(long userId, long deviceId, Date time) {
- Set<Long> result = new HashSet<>();
- Set<Long> deviceNotifications = getAllDeviceItems(deviceId);
- for (long itemId : getUserItems(userId)) {
- if (getById(itemId).getAlways() || deviceNotifications.contains(itemId)) {
- long calendarId = getById(itemId).getCalendarId();
- Calendar calendar = calendarId != 0 ? Context.getCalendarManager().getById(calendarId) : null;
- if (calendar == null || calendar.checkMoment(time)) {
- result.add(itemId);
- }
- }
- }
- return result;
+ @Inject
+ public NotificationManager(
+ Config config, Storage storage, CacheManager cacheManager, @Nullable EventForwarder eventForwarder,
+ NotificatorManager notificatorManager, @Nullable Geocoder geocoder) {
+ this.storage = storage;
+ this.cacheManager = cacheManager;
+ this.eventForwarder = eventForwarder;
+ this.notificatorManager = notificatorManager;
+ this.geocoder = geocoder;
+ geocodeOnRequest = config.getBoolean(Keys.GEOCODER_ON_REQUEST);
}
- public void updateEvent(Event event, Position position) {
+ private void updateEvent(Event event, Position position) {
try {
- getDataManager().addObject(event);
- } catch (SQLException error) {
+ event.setId(storage.addObject(event, new Request(new Columns.Exclude("id"))));
+ } catch (StorageException error) {
LOGGER.warn("Event save error", error);
}
- long deviceId = event.getDeviceId();
- Set<Long> users = Context.getPermissionsManager().getDeviceUsers(deviceId);
- Set<Long> usersToForward = null;
- if (Context.getEventForwarder() != null) {
- usersToForward = new HashSet<>();
- }
- for (long userId : users) {
- if ((event.getGeofenceId() == 0
- || Context.getGeofenceManager().checkItemPermission(userId, event.getGeofenceId()))
- && (event.getMaintenanceId() == 0
- || Context.getMaintenancesManager().checkItemPermission(userId, event.getMaintenanceId()))) {
- if (usersToForward != null) {
- usersToForward.add(userId);
- }
- final Set<String> notificators = new HashSet<>();
- for (long notificationId : getEffectiveNotifications(userId, deviceId, event.getEventTime())) {
- Notification notification = getById(notificationId);
- if (getById(notificationId).getType().equals(event.getType())) {
- boolean filter = false;
- if (event.getType().equals(Event.TYPE_ALARM)) {
- String alarmsAttribute = notification.getString("alarms");
- if (alarmsAttribute == null) {
- filter = true;
- } else {
- List<String> alarms = Arrays.asList(alarmsAttribute.split(","));
- filter = !alarms.contains(event.getString(Position.KEY_ALARM));
- }
- }
- if (!filter) {
- notificators.addAll(notification.getNotificatorsTypes());
+ var notifications = cacheManager.getDeviceObjects(event.getDeviceId(), Notification.class).stream()
+ .filter(notification -> notification.getType().equals(event.getType()))
+ .filter(notification -> {
+ if (event.getType().equals(Event.TYPE_ALARM)) {
+ String alarmsAttribute = notification.getString("alarms");
+ if (alarmsAttribute != null) {
+ return Arrays.asList(alarmsAttribute.split(","))
+ .contains(event.getString(Position.KEY_ALARM));
}
+ return false;
}
- }
-
- if (position != null && position.getAddress() == null
- && geocodeOnRequest && Context.getGeocoder() != null) {
- position.setAddress(Context.getGeocoder()
- .getAddress(position.getLatitude(), position.getLongitude(), null));
- }
+ return true;
+ })
+ .filter(notification -> {
+ long calendarId = notification.getCalendarId();
+ Calendar calendar = calendarId != 0 ? cacheManager.getObject(Calendar.class, calendarId) : null;
+ return calendar == null || calendar.checkMoment(event.getEventTime());
+ })
+ .collect(Collectors.toUnmodifiableList());
- for (String notificator : notificators) {
- Context.getNotificatorManager().getNotificator(notificator).sendAsync(userId, event, position);
- }
+ if (!notifications.isEmpty()) {
+ if (position != null && position.getAddress() == null && geocodeOnRequest && geocoder != null) {
+ position.setAddress(geocoder.getAddress(position.getLatitude(), position.getLongitude(), null));
}
+
+ notifications.forEach(notification -> {
+ cacheManager.getNotificationUsers(notification.getId(), event.getDeviceId()).forEach(user -> {
+ for (String notificator : notification.getNotificatorsTypes()) {
+ try {
+ notificatorManager.getNotificator(notificator).send(user, event, position);
+ } catch (MessageException | InterruptedException exception) {
+ LOGGER.warn("Notification failed", exception);
+ }
+ }
+ });
+ });
}
- if (Context.getEventForwarder() != null) {
- Context.getEventForwarder().forwardEvent(event, position, usersToForward);
- }
+
+ forwardEvent(event, position);
}
- public void updateEvents(Map<Event, Position> events) {
- for (Entry<Event, Position> event : events.entrySet()) {
- updateEvent(event.getKey(), event.getValue());
+ private void forwardEvent(Event event, Position position) {
+ if (eventForwarder != null) {
+ EventData eventData = new EventData();
+ eventData.setEvent(event);
+ eventData.setPosition(position);
+ eventData.setDevice(cacheManager.getObject(Device.class, event.getDeviceId()));
+ if (event.getGeofenceId() != 0) {
+ eventData.setGeofence(cacheManager.getObject(Geofence.class, event.getGeofenceId()));
+ }
+ if (event.getMaintenanceId() != 0) {
+ eventData.setMaintenance(cacheManager.getObject(Maintenance.class, event.getMaintenanceId()));
+ }
+ eventForwarder.forward(eventData, (success, throwable) -> {
+ if (!success) {
+ LOGGER.warn("Event forwarding failed", throwable);
+ }
+ });
}
}
- public Set<Typed> getAllNotificationTypes() {
- Set<Typed> types = new HashSet<>();
- Field[] fields = Event.class.getDeclaredFields();
- for (Field field : fields) {
- if (Modifier.isStatic(field.getModifiers()) && field.getName().startsWith("TYPE_")) {
- try {
- types.add(new Typed(field.get(null).toString()));
- } catch (IllegalArgumentException | IllegalAccessException error) {
- LOGGER.warn("Get event types error", error);
- }
+ public void updateEvents(Map<Event, Position> events) {
+ for (Entry<Event, Position> entry : events.entrySet()) {
+ Event event = entry.getKey();
+ Position position = entry.getValue();
+ try {
+ cacheManager.addDevice(event.getDeviceId());
+ updateEvent(event, position);
+ } catch (StorageException e) {
+ throw new RuntimeException(e);
+ } finally {
+ cacheManager.removeDevice(event.getDeviceId());
}
}
- return types;
}
}
diff --git a/src/main/java/org/traccar/database/PermissionsManager.java b/src/main/java/org/traccar/database/PermissionsManager.java
deleted file mode 100644
index 32464cf90..000000000
--- a/src/main/java/org/traccar/database/PermissionsManager.java
+++ /dev/null
@@ -1,516 +0,0 @@
-/*
- * Copyright 2015 - 2021 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.database;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.traccar.Context;
-import org.traccar.model.Attribute;
-import org.traccar.model.BaseModel;
-import org.traccar.model.Calendar;
-import org.traccar.model.Command;
-import org.traccar.model.Device;
-import org.traccar.model.Driver;
-import org.traccar.model.Geofence;
-import org.traccar.model.Group;
-import org.traccar.model.Maintenance;
-import org.traccar.model.ManagedUser;
-import org.traccar.model.Notification;
-import org.traccar.model.Order;
-import org.traccar.model.Permission;
-import org.traccar.model.Server;
-import org.traccar.model.User;
-
-import java.sql.SQLException;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-public class PermissionsManager {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(PermissionsManager.class);
-
- private final DataManager dataManager;
- private final UsersManager usersManager;
-
- private volatile Server server;
-
- private final ReadWriteLock lock = new ReentrantReadWriteLock();
-
- private final Map<Long, Set<Long>> groupPermissions = new HashMap<>();
- private final Map<Long, Set<Long>> devicePermissions = new HashMap<>();
- private final Map<Long, Set<Long>> deviceUsers = new HashMap<>();
- private final Map<Long, Set<Long>> groupDevices = new HashMap<>();
-
- public PermissionsManager(DataManager dataManager, UsersManager usersManager) {
- this.dataManager = dataManager;
- this.usersManager = usersManager;
- refreshServer();
- refreshDeviceAndGroupPermissions();
- }
-
- protected final void readLock() {
- lock.readLock().lock();
- }
-
- protected final void readUnlock() {
- lock.readLock().unlock();
- }
-
- protected final void writeLock() {
- lock.writeLock().lock();
- }
-
- protected final void writeUnlock() {
- lock.writeLock().unlock();
- }
-
- public User getUser(long userId) {
- readLock();
- try {
- return usersManager.getById(userId);
- } finally {
- readUnlock();
- }
- }
-
- public Set<Long> getGroupPermissions(long userId) {
- readLock();
- try {
- if (!groupPermissions.containsKey(userId)) {
- groupPermissions.put(userId, new HashSet<>());
- }
- return groupPermissions.get(userId);
- } finally {
- readUnlock();
- }
- }
-
- public Set<Long> getDevicePermissions(long userId) {
- readLock();
- try {
- if (!devicePermissions.containsKey(userId)) {
- devicePermissions.put(userId, new HashSet<>());
- }
- return devicePermissions.get(userId);
- } finally {
- readUnlock();
- }
- }
-
- private Set<Long> getAllDeviceUsers(long deviceId) {
- readLock();
- try {
- if (!deviceUsers.containsKey(deviceId)) {
- deviceUsers.put(deviceId, new HashSet<>());
- }
- return deviceUsers.get(deviceId);
- } finally {
- readUnlock();
- }
- }
-
- public Set<Long> getDeviceUsers(long deviceId) {
- Device device = Context.getIdentityManager().getById(deviceId);
- if (device != null && !device.getDisabled()) {
- return getAllDeviceUsers(deviceId);
- } else {
- Set<Long> result = new HashSet<>();
- for (long userId : getAllDeviceUsers(deviceId)) {
- if (getUserAdmin(userId)) {
- result.add(userId);
- }
- }
- return result;
- }
- }
-
- public Set<Long> getGroupDevices(long groupId) {
- readLock();
- try {
- if (!groupDevices.containsKey(groupId)) {
- groupDevices.put(groupId, new HashSet<>());
- }
- return groupDevices.get(groupId);
- } finally {
- readUnlock();
- }
- }
-
- public void refreshServer() {
- try {
- server = dataManager.getServer();
- } catch (SQLException error) {
- LOGGER.warn("Refresh server config error", error);
- }
- }
-
- public final void refreshDeviceAndGroupPermissions() {
- writeLock();
- try {
- groupPermissions.clear();
- devicePermissions.clear();
- try {
- GroupTree groupTree = new GroupTree(Context.getGroupsManager().getItems(
- Context.getGroupsManager().getAllItems()),
- Context.getDeviceManager().getAllDevices());
- for (Permission groupPermission : dataManager.getPermissions(User.class, Group.class)) {
- Set<Long> userGroupPermissions = getGroupPermissions(groupPermission.getOwnerId());
- Set<Long> userDevicePermissions = getDevicePermissions(groupPermission.getOwnerId());
- userGroupPermissions.add(groupPermission.getPropertyId());
- for (Group group : groupTree.getGroups(groupPermission.getPropertyId())) {
- userGroupPermissions.add(group.getId());
- }
- for (Device device : groupTree.getDevices(groupPermission.getPropertyId())) {
- userDevicePermissions.add(device.getId());
- }
- }
-
- for (Permission devicePermission : dataManager.getPermissions(User.class, Device.class)) {
- getDevicePermissions(devicePermission.getOwnerId()).add(devicePermission.getPropertyId());
- }
-
- groupDevices.clear();
- for (long groupId : Context.getGroupsManager().getAllItems()) {
- for (Device device : groupTree.getDevices(groupId)) {
- getGroupDevices(groupId).add(device.getId());
- }
- }
-
- } catch (SQLException | ClassNotFoundException error) {
- LOGGER.warn("Refresh device permissions error", error);
- }
-
- deviceUsers.clear();
- for (Map.Entry<Long, Set<Long>> entry : devicePermissions.entrySet()) {
- for (long deviceId : entry.getValue()) {
- getAllDeviceUsers(deviceId).add(entry.getKey());
- }
- }
- } finally {
- writeUnlock();
- }
- }
-
- public boolean getUserAdmin(long userId) {
- User user = getUser(userId);
- return user != null && user.getAdministrator();
- }
-
- public void checkAdmin(long userId) throws SecurityException {
- if (!getUserAdmin(userId)) {
- throw new SecurityException("Admin access required");
- }
- }
-
- public boolean getUserManager(long userId) {
- User user = getUser(userId);
- return user != null && user.getUserLimit() != 0;
- }
-
- public void checkManager(long userId) throws SecurityException {
- if (!getUserManager(userId)) {
- throw new SecurityException("Manager access required");
- }
- }
-
- public void checkManager(long userId, long managedUserId) throws SecurityException {
- checkManager(userId);
- if (!usersManager.getUserItems(userId).contains(managedUserId)) {
- throw new SecurityException("User access denied");
- }
- }
-
- public void checkUserLimit(long userId) throws SecurityException {
- int userLimit = getUser(userId).getUserLimit();
- if (userLimit != -1 && usersManager.getUserItems(userId).size() >= userLimit) {
- throw new SecurityException("Manager user limit reached");
- }
- }
-
- public void checkDeviceLimit(long userId) throws SecurityException {
- int deviceLimit = getUser(userId).getDeviceLimit();
- if (deviceLimit != -1) {
- int deviceCount;
- if (getUserManager(userId)) {
- deviceCount = Context.getDeviceManager().getAllManagedItems(userId).size();
- } else {
- deviceCount = Context.getDeviceManager().getAllUserItems(userId).size();
- }
- if (deviceCount >= deviceLimit) {
- throw new SecurityException("User device limit reached");
- }
- }
- }
-
- public boolean getUserReadonly(long userId) {
- User user = getUser(userId);
- return user != null && user.getReadonly();
- }
-
- public boolean getUserDeviceReadonly(long userId) {
- User user = getUser(userId);
- return user != null && user.getDeviceReadonly();
- }
-
- public boolean getUserLimitCommands(long userId) {
- User user = getUser(userId);
- return user != null && user.getLimitCommands();
- }
-
- public void checkReadonly(long userId) throws SecurityException {
- if (!getUserAdmin(userId) && (server.getReadonly() || getUserReadonly(userId))) {
- throw new SecurityException("Account is readonly");
- }
- }
-
- public void checkDeviceReadonly(long userId) throws SecurityException {
- if (!getUserAdmin(userId) && (server.getDeviceReadonly() || getUserDeviceReadonly(userId))) {
- throw new SecurityException("Account is device readonly");
- }
- }
-
- public void checkLimitCommands(long userId) throws SecurityException {
- if (!getUserAdmin(userId) && (server.getLimitCommands() || getUserLimitCommands(userId))) {
- throw new SecurityException("Account has limit sending commands");
- }
- }
-
- public void checkUserDeviceCommand(long userId, long deviceId, long commandId) throws SecurityException {
- if (!getUserAdmin(userId) && Context.getCommandsManager().checkDeviceCommand(deviceId, commandId)) {
- throw new SecurityException("Command can not be sent to this device");
- }
- }
-
- public void checkUserEnabled(long userId) throws SecurityException {
- User user = getUser(userId);
- if (user == null) {
- throw new SecurityException("Unknown account");
- }
- if (user.getDisabled()) {
- throw new SecurityException("Account is disabled");
- }
- if (user.getExpirationTime() != null && System.currentTimeMillis() > user.getExpirationTime().getTime()) {
- throw new SecurityException("Account has expired");
- }
- }
-
- public void checkUserUpdate(long userId, User before, User after) throws SecurityException {
- if (before.getAdministrator() != after.getAdministrator()
- || before.getDeviceLimit() != after.getDeviceLimit()
- || before.getUserLimit() != after.getUserLimit()) {
- checkAdmin(userId);
- }
- User user = getUser(userId);
- if (user != null && user.getExpirationTime() != null
- && (after.getExpirationTime() == null
- || user.getExpirationTime().compareTo(after.getExpirationTime()) < 0)) {
- checkAdmin(userId);
- }
- if (before.getReadonly() != after.getReadonly()
- || before.getDeviceReadonly() != after.getDeviceReadonly()
- || before.getDisabled() != after.getDisabled()
- || before.getLimitCommands() != after.getLimitCommands()) {
- if (userId == after.getId()) {
- checkAdmin(userId);
- }
- if (!getUserAdmin(userId)) {
- checkManager(userId);
- }
- }
- }
-
- public void checkUser(long userId, long managedUserId) throws SecurityException {
- if (userId != managedUserId && !getUserAdmin(userId)) {
- checkManager(userId, managedUserId);
- }
- }
-
- public void checkGroup(long userId, long groupId) throws SecurityException {
- if (!getGroupPermissions(userId).contains(groupId) && !getUserAdmin(userId)) {
- checkManager(userId);
- for (long managedUserId : usersManager.getUserItems(userId)) {
- if (getGroupPermissions(managedUserId).contains(groupId)) {
- return;
- }
- }
- throw new SecurityException("Group access denied");
- }
- }
-
- public void checkDevice(long userId, long deviceId) throws SecurityException {
- if (!Context.getDeviceManager().getUserItems(userId).contains(deviceId) && !getUserAdmin(userId)) {
- checkManager(userId);
- for (long managedUserId : usersManager.getUserItems(userId)) {
- if (Context.getDeviceManager().getUserItems(managedUserId).contains(deviceId)) {
- return;
- }
- }
- throw new SecurityException("Device access denied");
- }
- }
-
- public void checkRegistration(long userId) {
- if (!server.getRegistration() && !getUserAdmin(userId)) {
- throw new SecurityException("Registration disabled");
- }
- }
-
- public void checkPermission(Class<?> object, long userId, long objectId)
- throws SecurityException {
- SimpleObjectManager<? extends BaseModel> manager = null;
-
- if (object.equals(Device.class)) {
- checkDevice(userId, objectId);
- } else if (object.equals(Group.class)) {
- checkGroup(userId, objectId);
- } else if (object.equals(User.class) || object.equals(ManagedUser.class)) {
- checkUser(userId, objectId);
- } else if (object.equals(Geofence.class)) {
- manager = Context.getGeofenceManager();
- } else if (object.equals(Attribute.class)) {
- manager = Context.getAttributesManager();
- } else if (object.equals(Driver.class)) {
- manager = Context.getDriversManager();
- } else if (object.equals(Calendar.class)) {
- manager = Context.getCalendarManager();
- } else if (object.equals(Command.class)) {
- manager = Context.getCommandsManager();
- } else if (object.equals(Maintenance.class)) {
- manager = Context.getMaintenancesManager();
- } else if (object.equals(Notification.class)) {
- manager = Context.getNotificationManager();
- } else if (object.equals(Order.class)) {
- manager = Context.getOrderManager();
- } else {
- throw new IllegalArgumentException("Unknown object type");
- }
-
- if (manager != null && !manager.checkItemPermission(userId, objectId) && !getUserAdmin(userId)) {
- checkManager(userId);
- for (long managedUserId : usersManager.getManagedItems(userId)) {
- if (manager.checkItemPermission(managedUserId, objectId)) {
- return;
- }
- }
- throw new SecurityException("Type " + object + " access denied");
- }
- }
-
- public void refreshAllUsersPermissions() {
- if (Context.getGeofenceManager() != null) {
- Context.getGeofenceManager().refreshUserItems();
- }
- Context.getCalendarManager().refreshUserItems();
- Context.getDriversManager().refreshUserItems();
- Context.getAttributesManager().refreshUserItems();
- Context.getCommandsManager().refreshUserItems();
- Context.getMaintenancesManager().refreshUserItems();
- if (Context.getNotificationManager() != null) {
- Context.getNotificationManager().refreshUserItems();
- }
- }
-
- public void refreshAllExtendedPermissions() {
- if (Context.getGeofenceManager() != null) {
- Context.getGeofenceManager().refreshExtendedPermissions();
- }
- Context.getDriversManager().refreshExtendedPermissions();
- Context.getAttributesManager().refreshExtendedPermissions();
- Context.getCommandsManager().refreshExtendedPermissions();
- Context.getMaintenancesManager().refreshExtendedPermissions();
- }
-
- public void refreshPermissions(Permission permission) {
- if (permission.getOwnerClass().equals(User.class)) {
- if (permission.getPropertyClass().equals(Device.class)
- || permission.getPropertyClass().equals(Group.class)) {
- refreshDeviceAndGroupPermissions();
- refreshAllExtendedPermissions();
- } else if (permission.getPropertyClass().equals(ManagedUser.class)) {
- usersManager.refreshUserItems();
- } else if (permission.getPropertyClass().equals(Geofence.class) && Context.getGeofenceManager() != null) {
- Context.getGeofenceManager().refreshUserItems();
- } else if (permission.getPropertyClass().equals(Driver.class)) {
- Context.getDriversManager().refreshUserItems();
- } else if (permission.getPropertyClass().equals(Attribute.class)) {
- Context.getAttributesManager().refreshUserItems();
- } else if (permission.getPropertyClass().equals(Calendar.class)) {
- Context.getCalendarManager().refreshUserItems();
- } else if (permission.getPropertyClass().equals(Command.class)) {
- Context.getCommandsManager().refreshUserItems();
- } else if (permission.getPropertyClass().equals(Maintenance.class)) {
- Context.getMaintenancesManager().refreshUserItems();
- } else if (permission.getPropertyClass().equals(Order.class)) {
- Context.getOrderManager().refreshUserItems();
- } else if (permission.getPropertyClass().equals(Notification.class)
- && Context.getNotificationManager() != null) {
- Context.getNotificationManager().refreshUserItems();
- }
- } else if (permission.getOwnerClass().equals(Device.class) || permission.getOwnerClass().equals(Group.class)) {
- if (permission.getPropertyClass().equals(Geofence.class) && Context.getGeofenceManager() != null) {
- Context.getGeofenceManager().refreshExtendedPermissions();
- } else if (permission.getPropertyClass().equals(Driver.class)) {
- Context.getDriversManager().refreshExtendedPermissions();
- } else if (permission.getPropertyClass().equals(Attribute.class)) {
- Context.getAttributesManager().refreshExtendedPermissions();
- } else if (permission.getPropertyClass().equals(Command.class)) {
- Context.getCommandsManager().refreshExtendedPermissions();
- } else if (permission.getPropertyClass().equals(Maintenance.class)) {
- Context.getMaintenancesManager().refreshExtendedPermissions();
- } else if (permission.getPropertyClass().equals(Order.class)) {
- Context.getOrderManager().refreshExtendedPermissions();
- } else if (permission.getPropertyClass().equals(Notification.class)
- && Context.getNotificationManager() != null) {
- Context.getNotificationManager().refreshExtendedPermissions();
- }
- }
- }
-
- public Server getServer() {
- return server;
- }
-
- public void updateServer(Server server) throws SQLException {
- dataManager.updateObject(server);
- this.server = server;
- }
-
- public User login(String email, String password) throws SQLException {
- User user = dataManager.login(email, password);
- if (user != null) {
- checkUserEnabled(user.getId());
- return getUser(user.getId());
- }
- return null;
- }
-
- public Object lookupAttribute(long userId, String key, Object defaultValue) {
- Object preference;
- Object serverPreference = server.getAttributes().get(key);
- Object userPreference = getUser(userId).getAttributes().get(key);
- if (server.getForceSettings()) {
- preference = serverPreference != null ? serverPreference : userPreference;
- } else {
- preference = userPreference != null ? userPreference : serverPreference;
- }
- return preference != null ? preference : defaultValue;
- }
-
-}
diff --git a/src/main/java/org/traccar/database/SimpleObjectManager.java b/src/main/java/org/traccar/database/SimpleObjectManager.java
deleted file mode 100644
index eb8284d4e..000000000
--- a/src/main/java/org/traccar/database/SimpleObjectManager.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
- * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.database;
-
-import java.sql.SQLException;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.traccar.Context;
-import org.traccar.model.BaseModel;
-import org.traccar.model.Permission;
-import org.traccar.model.User;
-
-public abstract class SimpleObjectManager<T extends BaseModel> extends BaseObjectManager<T>
- implements ManagableObjects {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(SimpleObjectManager.class);
-
- private Map<Long, Set<Long>> userItems;
-
- protected SimpleObjectManager(DataManager dataManager, Class<T> baseClass) {
- super(dataManager, baseClass);
- }
-
- @Override
- public final Set<Long> getUserItems(long userId) {
- try {
- readLock();
- Set<Long> result = userItems.get(userId);
- if (result != null) {
- return new HashSet<>(result);
- } else {
- return new HashSet<>();
- }
- } finally {
- readUnlock();
- }
- }
-
- @Override
- public Set<Long> getManagedItems(long userId) {
- Set<Long> result = getUserItems(userId);
- for (long managedUserId : Context.getUsersManager().getUserItems(userId)) {
- result.addAll(getUserItems(managedUserId));
- }
- return result;
- }
-
- public final boolean checkItemPermission(long userId, long itemId) {
- return getUserItems(userId).contains(itemId);
- }
-
- @Override
- public void refreshItems() {
- super.refreshItems();
- refreshUserItems();
- }
-
- public final void refreshUserItems() {
- if (getDataManager() != null) {
- try {
- writeLock();
- userItems = new ConcurrentHashMap<>();
- for (Permission permission : getDataManager().getPermissions(User.class, getBaseClass())) {
- Set<Long> items = userItems.computeIfAbsent(permission.getOwnerId(), key -> new HashSet<>());
- items.add(permission.getPropertyId());
- }
- } catch (SQLException | ClassNotFoundException error) {
- LOGGER.warn("Error getting permissions", error);
- } finally {
- writeUnlock();
- }
- }
- }
-
- @Override
- public void removeItem(long itemId) throws SQLException {
- super.removeItem(itemId);
- refreshUserItems();
- }
-
-}
diff --git a/src/main/java/org/traccar/database/StatisticsManager.java b/src/main/java/org/traccar/database/StatisticsManager.java
index 4ad6d9d5c..e0995dabc 100644
--- a/src/main/java/org/traccar/database/StatisticsManager.java
+++ b/src/main/java/org/traccar/database/StatisticsManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,12 +23,16 @@ import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.helper.DateUtil;
import org.traccar.model.Statistics;
+import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
+import 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 java.sql.SQLException;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
@@ -37,6 +41,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
+@Singleton
public class StatisticsManager {
private static final Logger LOGGER = LoggerFactory.getLogger(StatisticsManager.class);
@@ -44,7 +49,7 @@ public class StatisticsManager {
private static final int SPLIT_MODE = Calendar.DAY_OF_MONTH;
private final Config config;
- private final DataManager dataManager;
+ private final Storage storage;
private final Client client;
private final ObjectMapper objectMapper;
@@ -62,9 +67,9 @@ public class StatisticsManager {
private int geolocationRequests;
@Inject
- public StatisticsManager(Config config, DataManager dataManager, Client client, ObjectMapper objectMapper) {
+ public StatisticsManager(Config config, Storage storage, Client client, ObjectMapper objectMapper) {
this.config = config;
- this.dataManager = dataManager;
+ this.storage = storage;
this.client = client;
this.objectMapper = objectMapper;
}
@@ -105,8 +110,8 @@ public class StatisticsManager {
}
try {
- dataManager.addObject(statistics);
- } catch (SQLException e) {
+ storage.addObject(statistics, new Request(new Columns.Exclude("id")));
+ } catch (StorageException e) {
LOGGER.warn("Error saving statistics", e);
}
diff --git a/src/main/java/org/traccar/database/UsersManager.java b/src/main/java/org/traccar/database/UsersManager.java
deleted file mode 100644
index b741a85b6..000000000
--- a/src/main/java/org/traccar/database/UsersManager.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
- * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.database;
-
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.traccar.model.User;
-
-public class UsersManager extends SimpleObjectManager<User> {
-
- private Map<String, User> usersTokens;
-
- public UsersManager(DataManager dataManager) {
- super(dataManager, User.class);
- if (usersTokens == null) {
- usersTokens = new ConcurrentHashMap<>();
- }
- }
-
- private void putToken(User user) {
- if (usersTokens == null) {
- usersTokens = new ConcurrentHashMap<>();
- }
- if (user.getToken() != null) {
- usersTokens.put(user.getToken(), user);
- }
- }
-
- @Override
- protected void addNewItem(User user) {
- super.addNewItem(user);
- putToken(user);
- }
-
- @Override
- protected void updateCachedItem(User user) {
- User cachedUser = getById(user.getId());
- super.updateCachedItem(user);
- putToken(user);
- if (cachedUser.getToken() != null && !cachedUser.getToken().equals(user.getToken())) {
- usersTokens.remove(cachedUser.getToken());
- }
- }
-
- @Override
- protected void removeCachedItem(long userId) {
- User cachedUser = getById(userId);
- if (cachedUser != null) {
- String userToken = cachedUser.getToken();
- super.removeCachedItem(userId);
- if (userToken != null) {
- usersTokens.remove(userToken);
- }
- }
- }
-
- @Override
- public Set<Long> getManagedItems(long userId) {
- Set<Long> result = getUserItems(userId);
- result.add(userId);
- return result;
- }
-
- public User getUserByToken(String token) {
- return usersTokens.get(token);
- }
-
-}
diff --git a/src/main/java/org/traccar/forward/EventData.java b/src/main/java/org/traccar/forward/EventData.java
new file mode 100644
index 000000000..4471b10b3
--- /dev/null
+++ b/src/main/java/org/traccar/forward/EventData.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.forward;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import org.traccar.model.Device;
+import org.traccar.model.Event;
+import org.traccar.model.Geofence;
+import org.traccar.model.Maintenance;
+import org.traccar.model.Position;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class EventData {
+
+ private Event event;
+
+ public Event getEvent() {
+ return event;
+ }
+
+ public void setEvent(Event event) {
+ this.event = event;
+ }
+
+ private Position position;
+
+ public Position getPosition() {
+ return position;
+ }
+
+ public void setPosition(Position position) {
+ this.position = position;
+ }
+
+ private Device device;
+
+ public Device getDevice() {
+ return device;
+ }
+
+ public void setDevice(Device device) {
+ this.device = device;
+ }
+
+ private Geofence geofence;
+
+ public Geofence getGeofence() {
+ return geofence;
+ }
+
+ public void setGeofence(Geofence geofence) {
+ this.geofence = geofence;
+ }
+
+ private Maintenance maintenance;
+
+ public Maintenance getMaintenance() {
+ return maintenance;
+ }
+
+ public void setMaintenance(Maintenance maintenance) {
+ this.maintenance = maintenance;
+ }
+
+}
diff --git a/src/main/java/org/traccar/forward/EventForwarder.java b/src/main/java/org/traccar/forward/EventForwarder.java
new file mode 100644
index 000000000..1f991c0b5
--- /dev/null
+++ b/src/main/java/org/traccar/forward/EventForwarder.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.forward;
+
+public interface EventForwarder {
+ void forward(EventData eventData, ResultHandler resultHandler);
+}
diff --git a/src/main/java/org/traccar/forward/EventForwarderJson.java b/src/main/java/org/traccar/forward/EventForwarderJson.java
new file mode 100644
index 000000000..7527d568a
--- /dev/null
+++ b/src/main/java/org/traccar/forward/EventForwarderJson.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.forward;
+
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.InvocationCallback;
+import javax.ws.rs.core.Response;
+
+public class EventForwarderJson implements EventForwarder {
+
+ private final String url;
+ private final String header;
+
+ private final Client client;
+
+ public EventForwarderJson(Config config, Client client) {
+ this.client = client;
+ url = config.getString(Keys.EVENT_FORWARD_URL);
+ header = config.getString(Keys.EVENT_FORWARD_HEADERS);
+ }
+
+ @Override
+ public void forward(EventData eventData, ResultHandler resultHandler) {
+ var requestBuilder = client.target(url).request();
+
+ if (header != null && !header.isEmpty()) {
+ for (String line: header.split("\\r?\\n")) {
+ String[] values = line.split(":", 2);
+ requestBuilder.header(values[0].trim(), values[1].trim());
+ }
+ }
+
+ requestBuilder.async().post(Entity.json(eventData), new InvocationCallback<Response>() {
+ @Override
+ public void completed(Response response) {
+ if (response.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL) {
+ resultHandler.onResult(true, null);
+ } else {
+ int code = response.getStatusInfo().getStatusCode();
+ resultHandler.onResult(false, new RuntimeException("HTTP code " + code));
+ }
+ }
+
+ @Override
+ public void failed(Throwable throwable) {
+ resultHandler.onResult(false, throwable);
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/forward/EventForwarderKafka.java b/src/main/java/org/traccar/forward/EventForwarderKafka.java
new file mode 100644
index 000000000..e65c3a51b
--- /dev/null
+++ b/src/main/java/org/traccar/forward/EventForwarderKafka.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.forward;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.kafka.clients.producer.KafkaProducer;
+import org.apache.kafka.clients.producer.Producer;
+import org.apache.kafka.clients.producer.ProducerRecord;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+
+import java.util.Properties;
+
+public class EventForwarderKafka implements EventForwarder {
+
+ private final Producer<String, String> producer;
+ private final ObjectMapper objectMapper;
+
+ private final String topic;
+
+ public EventForwarderKafka(Config config, ObjectMapper objectMapper) {
+ this.objectMapper = objectMapper;
+ Properties properties = new Properties();
+ properties.put("bootstrap.servers", config.getString(Keys.EVENT_FORWARD_URL));
+ properties.put("acks", "all");
+ properties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
+ properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
+ producer = new KafkaProducer<>(properties);
+ topic = config.getString(Keys.EVENT_FORWARD_TOPIC);
+ }
+
+ @Override
+ public void forward(EventData eventData, ResultHandler resultHandler) {
+ try {
+ String key = Long.toString(eventData.getDevice().getId());
+ String value = objectMapper.writeValueAsString(eventData);
+ producer.send(new ProducerRecord<>(topic, key, value));
+ resultHandler.onResult(true, null);
+ } catch (JsonProcessingException e) {
+ resultHandler.onResult(false, e);
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/forward/EventForwarderMqtt.java b/src/main/java/org/traccar/forward/EventForwarderMqtt.java
new file mode 100644
index 000000000..dc95cb4e2
--- /dev/null
+++ b/src/main/java/org/traccar/forward/EventForwarderMqtt.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.forward;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.hivemq.client.mqtt.datatypes.MqttQos;
+import com.hivemq.client.mqtt.mqtt5.Mqtt5AsyncClient;
+import com.hivemq.client.mqtt.mqtt5.Mqtt5Client;
+import com.hivemq.client.mqtt.mqtt5.message.auth.Mqtt5SimpleAuth;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.UUID;
+
+public class EventForwarderMqtt implements EventForwarder {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(EventForwarderMqtt.class);
+ private final Mqtt5AsyncClient client;
+ private final ObjectMapper objectMapper;
+
+ private final String topic;
+
+ public EventForwarderMqtt(Config config, ObjectMapper objectMapper) {
+ URI url;
+ try {
+ url = new URI(config.getString(Keys.EVENT_FORWARD_URL));
+ } catch (URISyntaxException e) {
+ throw new RuntimeException(e);
+ }
+
+ String userInfo = url.getUserInfo();
+ Mqtt5SimpleAuth simpleAuth = null;
+ if (userInfo != null) {
+ int delimiter = userInfo.indexOf(':');
+ if (delimiter == -1) {
+ throw new IllegalArgumentException("Wrong credentials. Should be in format \"username:password\"");
+ } else {
+ simpleAuth = Mqtt5SimpleAuth.builder()
+ .username(userInfo.substring(0, delimiter++))
+ .password(userInfo.substring(delimiter).getBytes())
+ .build();
+ }
+ }
+
+ String host = url.getHost();
+ int port = url.getPort();
+ client = Mqtt5Client.builder()
+ .identifier("traccar-" + UUID.randomUUID().toString())
+ .serverHost(host)
+ .serverPort(port)
+ .simpleAuth(simpleAuth)
+ .automaticReconnectWithDefaultConfig()
+ .buildAsync();
+
+ client.connectWith()
+ .send()
+ .whenComplete((message, e) -> {
+ if (e != null) {
+ throw new RuntimeException(e);
+ }
+ });
+
+ this.objectMapper = objectMapper;
+ topic = config.getString(Keys.EVENT_FORWARD_TOPIC);
+ }
+
+ @Override
+ public void forward(EventData eventData, ResultHandler resultHandler) {
+ byte[] payload;
+ try {
+ payload = objectMapper.writeValueAsString(eventData).getBytes();
+ } catch (JsonProcessingException e) {
+ resultHandler.onResult(false, e);
+ return;
+ }
+
+ client.publishWith()
+ .topic(topic)
+ .qos(MqttQos.AT_LEAST_ONCE)
+ .payload(payload)
+ .send()
+ .whenComplete((message, e) -> resultHandler.onResult(e == null, e));
+ }
+
+}
diff --git a/src/main/java/org/traccar/forward/PositionData.java b/src/main/java/org/traccar/forward/PositionData.java
new file mode 100644
index 000000000..784cf52f5
--- /dev/null
+++ b/src/main/java/org/traccar/forward/PositionData.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.forward;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import org.traccar.model.Device;
+import org.traccar.model.Position;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class PositionData {
+
+ private Position position;
+
+ public Position getPosition() {
+ return position;
+ }
+
+ public void setPosition(Position position) {
+ this.position = position;
+ }
+
+ private Device device;
+
+ public Device getDevice() {
+ return device;
+ }
+
+ public void setDevice(Device device) {
+ this.device = device;
+ }
+
+}
diff --git a/src/main/java/org/traccar/forward/PositionForwarder.java b/src/main/java/org/traccar/forward/PositionForwarder.java
new file mode 100644
index 000000000..58bd1dcc7
--- /dev/null
+++ b/src/main/java/org/traccar/forward/PositionForwarder.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.forward;
+
+public interface PositionForwarder {
+ void forward(PositionData positionData, ResultHandler resultHandler);
+}
diff --git a/src/main/java/org/traccar/forward/PositionForwarderJson.java b/src/main/java/org/traccar/forward/PositionForwarderJson.java
new file mode 100644
index 000000000..27b96308e
--- /dev/null
+++ b/src/main/java/org/traccar/forward/PositionForwarderJson.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.forward;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+
+import 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;
+
+public class PositionForwarderJson implements PositionForwarder {
+
+ private final String url;
+ private final String header;
+
+ private final Client client;
+ private final ObjectMapper objectMapper;
+
+ public PositionForwarderJson(Config config, Client client, ObjectMapper objectMapper) {
+ this.client = client;
+ this.objectMapper = objectMapper;
+ this.url = config.getString(Keys.FORWARD_URL);
+ this.header = config.getString(Keys.FORWARD_HEADER);
+ }
+
+ @Override
+ public void forward(PositionData positionData, ResultHandler resultHandler) {
+ var requestBuilder = client.target(url).request();
+
+ MediaType mediaType = MediaType.APPLICATION_JSON_TYPE;
+ if (header != null && !header.isEmpty()) {
+ for (String line: header.split("\\r?\\n")) {
+ String[] values = line.split(":", 2);
+ String headerName = values[0].trim();
+ String headerValue = values[1].trim();
+ if (headerName.equals(HttpHeaders.CONTENT_TYPE)) {
+ mediaType = MediaType.valueOf(headerValue);
+ } else {
+ requestBuilder.header(headerName, headerValue);
+ }
+ }
+ }
+
+ try {
+ var entity = Entity.entity(objectMapper.writeValueAsString(positionData), mediaType);
+ requestBuilder.async().post(entity, new InvocationCallback<Response>() {
+ @Override
+ public void completed(Response response) {
+ if (response.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL) {
+ resultHandler.onResult(true, null);
+ } else {
+ int code = response.getStatusInfo().getStatusCode();
+ resultHandler.onResult(false, new RuntimeException("HTTP code " + code));
+ }
+ }
+
+ @Override
+ public void failed(Throwable throwable) {
+ resultHandler.onResult(false, throwable);
+ }
+ });
+ } catch (JsonProcessingException e) {
+ resultHandler.onResult(false, e);
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/forward/PositionForwarderKafka.java b/src/main/java/org/traccar/forward/PositionForwarderKafka.java
new file mode 100644
index 000000000..7432e9364
--- /dev/null
+++ b/src/main/java/org/traccar/forward/PositionForwarderKafka.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.forward;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.kafka.clients.producer.KafkaProducer;
+import org.apache.kafka.clients.producer.Producer;
+import org.apache.kafka.clients.producer.ProducerRecord;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+
+import java.util.Properties;
+
+public class PositionForwarderKafka implements PositionForwarder {
+
+ private final Producer<String, String> producer;
+ private final ObjectMapper objectMapper;
+
+ private final String topic;
+
+ public PositionForwarderKafka(Config config, ObjectMapper objectMapper) {
+ this.objectMapper = objectMapper;
+ Properties properties = new Properties();
+ properties.put("bootstrap.servers", config.getString(Keys.FORWARD_URL));
+ properties.put("acks", "all");
+ properties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
+ properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
+ producer = new KafkaProducer<>(properties);
+ topic = config.getString(Keys.FORWARD_TOPIC);
+ }
+
+ @Override
+ public void forward(PositionData positionData, ResultHandler resultHandler) {
+ try {
+ String key = Long.toString(positionData.getDevice().getId());
+ String value = objectMapper.writeValueAsString(positionData);
+ producer.send(new ProducerRecord<>(topic, key, value));
+ resultHandler.onResult(true, null);
+ } catch (JsonProcessingException e) {
+ resultHandler.onResult(false, e);
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/forward/PositionForwarderUrl.java b/src/main/java/org/traccar/forward/PositionForwarderUrl.java
new file mode 100644
index 000000000..53cc7ad24
--- /dev/null
+++ b/src/main/java/org/traccar/forward/PositionForwarderUrl.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.forward;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.helper.Checksum;
+import org.traccar.model.Device;
+import org.traccar.model.Position;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.InvocationCallback;
+import javax.ws.rs.core.Response;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.Calendar;
+import java.util.Formatter;
+import java.util.Locale;
+import java.util.TimeZone;
+
+public class PositionForwarderUrl implements PositionForwarder {
+
+ private final String url;
+ private final String header;
+
+ private final Client client;
+ private final ObjectMapper objectMapper;
+
+ public PositionForwarderUrl(Config config, Client client, ObjectMapper objectMapper) {
+ this.client = client;
+ this.objectMapper = objectMapper;
+ this.url = config.getString(Keys.FORWARD_URL);
+ this.header = config.getString(Keys.FORWARD_HEADER);
+ }
+
+ @Override
+ public void forward(PositionData positionData, ResultHandler resultHandler) {
+ try {
+ String url = formatRequest(positionData);
+ var requestBuilder = client.target(url).request();
+
+ if (header != null && !header.isEmpty()) {
+ for (String line: header.split("\\r?\\n")) {
+ String[] values = line.split(":", 2);
+ String headerName = values[0].trim();
+ String headerValue = values[1].trim();
+ requestBuilder.header(headerName, headerValue);
+ }
+ }
+
+ requestBuilder.async().get(new InvocationCallback<Response>() {
+ @Override
+ public void completed(Response response) {
+ if (response.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL) {
+ resultHandler.onResult(true, null);
+ } else {
+ int code = response.getStatusInfo().getStatusCode();
+ resultHandler.onResult(false, new RuntimeException("HTTP code " + code));
+ }
+ }
+
+ @Override
+ public void failed(Throwable throwable) {
+ resultHandler.onResult(false, throwable);
+ }
+ });
+ } catch (UnsupportedEncodingException | JsonProcessingException e) {
+ resultHandler.onResult(false, e);
+ }
+ }
+
+ public String formatRequest(
+ PositionData positionData) throws UnsupportedEncodingException, JsonProcessingException {
+
+ Position position = positionData.getPosition();
+ Device device = positionData.getDevice();
+
+ String request = url
+ .replace("{name}", URLEncoder.encode(device.getName(), StandardCharsets.UTF_8))
+ .replace("{uniqueId}", device.getUniqueId())
+ .replace("{status}", device.getStatus())
+ .replace("{deviceId}", String.valueOf(position.getDeviceId()))
+ .replace("{protocol}", String.valueOf(position.getProtocol()))
+ .replace("{deviceTime}", String.valueOf(position.getDeviceTime().getTime()))
+ .replace("{fixTime}", String.valueOf(position.getFixTime().getTime()))
+ .replace("{valid}", String.valueOf(position.getValid()))
+ .replace("{latitude}", String.valueOf(position.getLatitude()))
+ .replace("{longitude}", String.valueOf(position.getLongitude()))
+ .replace("{altitude}", String.valueOf(position.getAltitude()))
+ .replace("{speed}", String.valueOf(position.getSpeed()))
+ .replace("{course}", String.valueOf(position.getCourse()))
+ .replace("{accuracy}", String.valueOf(position.getAccuracy()))
+ .replace("{statusCode}", calculateStatus(position));
+
+ if (position.getAddress() != null) {
+ request = request.replace(
+ "{address}", URLEncoder.encode(position.getAddress(), StandardCharsets.UTF_8));
+ }
+
+ if (request.contains("{attributes}")) {
+ String attributes = objectMapper.writeValueAsString(position.getAttributes());
+ request = request.replace(
+ "{attributes}", URLEncoder.encode(attributes, StandardCharsets.UTF_8));
+ }
+
+ if (request.contains("{gprmc}")) {
+ request = request.replace("{gprmc}", formatSentence(position));
+ }
+
+ return request;
+ }
+
+ private static String formatSentence(Position position) {
+
+ StringBuilder s = new StringBuilder("$GPRMC,");
+
+ try (Formatter f = new Formatter(s, Locale.ENGLISH)) {
+
+ Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"), Locale.ENGLISH);
+ calendar.setTimeInMillis(position.getFixTime().getTime());
+
+ f.format("%1$tH%1$tM%1$tS.%1$tL,A,", calendar);
+
+ double lat = position.getLatitude();
+ double lon = position.getLongitude();
+
+ f.format("%02d%07.4f,%c,", (int) Math.abs(lat), Math.abs(lat) % 1 * 60, lat < 0 ? 'S' : 'N');
+ f.format("%03d%07.4f,%c,", (int) Math.abs(lon), Math.abs(lon) % 1 * 60, lon < 0 ? 'W' : 'E');
+
+ f.format("%.2f,%.2f,", position.getSpeed(), position.getCourse());
+ f.format("%1$td%1$tm%1$ty,,", calendar);
+ }
+
+ s.append(Checksum.nmea(s.substring(1)));
+
+ return s.toString();
+ }
+
+ // OpenGTS status code
+ private String calculateStatus(Position position) {
+ if (position.hasAttribute(Position.KEY_ALARM)) {
+ return "0xF841"; // STATUS_PANIC_ON
+ } else if (position.getSpeed() < 1.0) {
+ return "0xF020"; // STATUS_LOCATION
+ } else {
+ return "0xF11C"; // STATUS_MOTION_MOVING
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/forward/ResultHandler.java b/src/main/java/org/traccar/forward/ResultHandler.java
new file mode 100644
index 000000000..009daf495
--- /dev/null
+++ b/src/main/java/org/traccar/forward/ResultHandler.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.forward;
+
+public interface ResultHandler {
+ void onResult(boolean success, Throwable throwable);
+}
diff --git a/src/main/java/org/traccar/geocoder/BanGeocoder.java b/src/main/java/org/traccar/geocoder/BanGeocoder.java
index b1f0900a4..f878a8bab 100644
--- a/src/main/java/org/traccar/geocoder/BanGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/BanGeocoder.java
@@ -22,11 +22,12 @@ package org.traccar.geocoder;
import javax.json.JsonArray;
import javax.json.JsonObject;
+import javax.ws.rs.client.Client;
public class BanGeocoder extends JsonGeocoder {
- public BanGeocoder(int cacheSize, AddressFormat addressFormat) {
- super("https://api-adresse.data.gouv.fr/reverse/?lat=%f&lon=%f", cacheSize, addressFormat);
+ public BanGeocoder(Client client, int cacheSize, AddressFormat addressFormat) {
+ super(client, "https://api-adresse.data.gouv.fr/reverse/?lat=%f&lon=%f", cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geocoder/BingMapsGeocoder.java b/src/main/java/org/traccar/geocoder/BingMapsGeocoder.java
index 32a26ee0c..01e33c2ea 100644
--- a/src/main/java/org/traccar/geocoder/BingMapsGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/BingMapsGeocoder.java
@@ -18,11 +18,12 @@ package org.traccar.geocoder;
import javax.json.JsonArray;
import javax.json.JsonObject;
+import javax.ws.rs.client.Client;
public class BingMapsGeocoder extends JsonGeocoder {
- public BingMapsGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) {
- super(url + "/Locations/%f,%f?key=" + key + "&include=ciso2", cacheSize, addressFormat);
+ public BingMapsGeocoder(Client client, String url, String key, int cacheSize, AddressFormat addressFormat) {
+ super(client, url + "/Locations/%f,%f?key=" + key + "&include=ciso2", cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geocoder/FactualGeocoder.java b/src/main/java/org/traccar/geocoder/FactualGeocoder.java
index f540eb8fe..384f46b0e 100644
--- a/src/main/java/org/traccar/geocoder/FactualGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/FactualGeocoder.java
@@ -17,6 +17,7 @@
package org.traccar.geocoder;
import javax.json.JsonObject;
+import javax.ws.rs.client.Client;
public class FactualGeocoder extends JsonGeocoder {
@@ -28,8 +29,8 @@ public class FactualGeocoder extends JsonGeocoder {
return url;
}
- public FactualGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) {
- super(formatUrl(url, key), cacheSize, addressFormat);
+ public FactualGeocoder(Client client, String url, String key, int cacheSize, AddressFormat addressFormat) {
+ super(client, formatUrl(url, key), cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geocoder/GeoapifyGeocoder.java b/src/main/java/org/traccar/geocoder/GeoapifyGeocoder.java
new file mode 100644
index 000000000..4748d6a2c
--- /dev/null
+++ b/src/main/java/org/traccar/geocoder/GeoapifyGeocoder.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.geocoder;
+
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.ws.rs.client.Client;
+
+public class GeoapifyGeocoder extends JsonGeocoder {
+
+ private static String formatUrl(String key, String language) {
+ String url = "https://api.geoapify.com/v1/geocode/reverse?format=json&lat=%f&lon=%f";
+ if (key != null) {
+ url += "&apiKey=" + key;
+ }
+ if (language != null) {
+ url += "&lang=" + language;
+ }
+ return url;
+ }
+
+ public GeoapifyGeocoder(Client client, String key, String language, int cacheSize, AddressFormat addressFormat) {
+ super(client, formatUrl(key, language), cacheSize, addressFormat);
+ }
+
+ @Override
+ public Address parseAddress(JsonObject json) {
+ JsonArray results = json.getJsonArray("results");
+ if (results.size() > 0) {
+ JsonObject result = results.getJsonObject(0);
+
+ Address address = new Address();
+
+ if (json.containsKey("formatted")) {
+ address.setFormattedAddress(json.getString("formatted"));
+ }
+
+ if (result.containsKey("housenumber")) {
+ address.setHouse(result.getString("housenumber"));
+ }
+ if (result.containsKey("street")) {
+ address.setStreet(result.getString("street"));
+ }
+ if (result.containsKey("suburb")) {
+ address.setSuburb(result.getString("suburb"));
+ }
+ if (result.containsKey("city")) {
+ address.setSettlement(result.getString("city"));
+ }
+ if (result.containsKey("district")) {
+ address.setDistrict(result.getString("district"));
+ }
+ if (result.containsKey("state")) {
+ address.setState(result.getString("state"));
+ }
+ if (result.containsKey("country_code")) {
+ address.setCountry(result.getString("country_code").toUpperCase());
+ }
+ if (result.containsKey("postcode")) {
+ address.setPostcode(result.getString("postcode"));
+ }
+
+ return address;
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/geocoder/GeocodeFarmGeocoder.java b/src/main/java/org/traccar/geocoder/GeocodeFarmGeocoder.java
index 39a3300a0..2af95910f 100644
--- a/src/main/java/org/traccar/geocoder/GeocodeFarmGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/GeocodeFarmGeocoder.java
@@ -16,6 +16,7 @@
package org.traccar.geocoder;
import javax.json.JsonObject;
+import javax.ws.rs.client.Client;
public class GeocodeFarmGeocoder extends JsonGeocoder {
@@ -30,8 +31,9 @@ public class GeocodeFarmGeocoder extends JsonGeocoder {
}
return url;
}
- public GeocodeFarmGeocoder(String key, String language, int cacheSize, AddressFormat addressFormat) {
- super(formatUrl(key, language), cacheSize, addressFormat);
+ public GeocodeFarmGeocoder(
+ Client client, String key, String language, int cacheSize, AddressFormat addressFormat) {
+ super(client, formatUrl(key, language), cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geocoder/GeocodeXyzGeocoder.java b/src/main/java/org/traccar/geocoder/GeocodeXyzGeocoder.java
index aca360c3d..96491ece3 100644
--- a/src/main/java/org/traccar/geocoder/GeocodeXyzGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/GeocodeXyzGeocoder.java
@@ -16,6 +16,7 @@
package org.traccar.geocoder;
import javax.json.JsonObject;
+import javax.ws.rs.client.Client;
public class GeocodeXyzGeocoder extends JsonGeocoder {
@@ -27,8 +28,8 @@ public class GeocodeXyzGeocoder extends JsonGeocoder {
return url;
}
- public GeocodeXyzGeocoder(String key, int cacheSize, AddressFormat addressFormat) {
- super(formatUrl(key), cacheSize, addressFormat);
+ public GeocodeXyzGeocoder(Client client, String key, int cacheSize, AddressFormat addressFormat) {
+ super(client, formatUrl(key), cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geocoder/Geocoder.java b/src/main/java/org/traccar/geocoder/Geocoder.java
index 587a27520..f4abe871a 100644
--- a/src/main/java/org/traccar/geocoder/Geocoder.java
+++ b/src/main/java/org/traccar/geocoder/Geocoder.java
@@ -15,6 +15,8 @@
*/
package org.traccar.geocoder;
+import org.traccar.database.StatisticsManager;
+
public interface Geocoder {
interface ReverseGeocoderCallback {
@@ -27,4 +29,6 @@ public interface Geocoder {
String getAddress(double latitude, double longitude, ReverseGeocoderCallback callback);
+ void setStatisticsManager(StatisticsManager statisticsManager);
+
}
diff --git a/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java b/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java
index b4881a006..0589eb000 100644
--- a/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java
@@ -16,6 +16,7 @@
package org.traccar.geocoder;
import javax.json.JsonObject;
+import javax.ws.rs.client.Client;
public class GisgraphyGeocoder extends JsonGeocoder {
@@ -27,8 +28,8 @@ public class GisgraphyGeocoder extends JsonGeocoder {
return url;
}
- public GisgraphyGeocoder(String url, int cacheSize, AddressFormat addressFormat) {
- super(formatUrl(url), cacheSize, addressFormat);
+ public GisgraphyGeocoder(Client client, String url, int cacheSize, AddressFormat addressFormat) {
+ super(client, formatUrl(url), cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geocoder/GoogleGeocoder.java b/src/main/java/org/traccar/geocoder/GoogleGeocoder.java
index 9494cab45..4d9ec8f36 100644
--- a/src/main/java/org/traccar/geocoder/GoogleGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/GoogleGeocoder.java
@@ -18,6 +18,7 @@ package org.traccar.geocoder;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.json.JsonString;
+import javax.ws.rs.client.Client;
public class GoogleGeocoder extends JsonGeocoder {
@@ -32,8 +33,8 @@ public class GoogleGeocoder extends JsonGeocoder {
return url;
}
- public GoogleGeocoder(String key, String language, int cacheSize, AddressFormat addressFormat) {
- super(formatUrl(key, language), cacheSize, addressFormat);
+ public GoogleGeocoder(Client client, String key, String language, int cacheSize, AddressFormat addressFormat) {
+ super(client, formatUrl(key, language), cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geocoder/HereGeocoder.java b/src/main/java/org/traccar/geocoder/HereGeocoder.java
index 40390e65b..eb639995e 100644
--- a/src/main/java/org/traccar/geocoder/HereGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/HereGeocoder.java
@@ -16,6 +16,7 @@
package org.traccar.geocoder;
import javax.json.JsonObject;
+import javax.ws.rs.client.Client;
public class HereGeocoder extends JsonGeocoder {
@@ -35,8 +36,9 @@ public class HereGeocoder extends JsonGeocoder {
}
public HereGeocoder(
- String url, String id, String key, String language, int cacheSize, AddressFormat addressFormat) {
- super(formatUrl(url, id, key, language), cacheSize, addressFormat);
+ Client client, String url, String id, String key, String language,
+ int cacheSize, AddressFormat addressFormat) {
+ super(client, formatUrl(url, id, key, language), cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geocoder/JsonGeocoder.java b/src/main/java/org/traccar/geocoder/JsonGeocoder.java
index f20aa79d6..6105e8cfd 100644
--- a/src/main/java/org/traccar/geocoder/JsonGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/JsonGeocoder.java
@@ -17,13 +17,11 @@ package org.traccar.geocoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.traccar.Context;
-import org.traccar.Main;
import org.traccar.database.StatisticsManager;
import javax.json.JsonObject;
import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.client.Invocation;
+import javax.ws.rs.client.Client;
import javax.ws.rs.client.InvocationCallback;
import java.util.AbstractMap;
import java.util.Collections;
@@ -34,16 +32,19 @@ public abstract class JsonGeocoder implements Geocoder {
private static final Logger LOGGER = LoggerFactory.getLogger(JsonGeocoder.class);
+ private final Client client;
private final String url;
private final AddressFormat addressFormat;
+ private StatisticsManager statisticsManager;
private Map<Map.Entry<Double, Double>, String> cache;
- public JsonGeocoder(String url, final int cacheSize, AddressFormat addressFormat) {
+ public JsonGeocoder(Client client, String url, final int cacheSize, AddressFormat addressFormat) {
+ this.client = client;
this.url = url;
this.addressFormat = addressFormat;
if (cacheSize > 0) {
- this.cache = Collections.synchronizedMap(new LinkedHashMap<Map.Entry<Double, Double>, String>() {
+ this.cache = Collections.synchronizedMap(new LinkedHashMap<>() {
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > cacheSize;
@@ -52,6 +53,11 @@ public abstract class JsonGeocoder implements Geocoder {
}
}
+ @Override
+ public void setStatisticsManager(StatisticsManager statisticsManager) {
+ this.statisticsManager = statisticsManager;
+ }
+
protected String readValue(JsonObject object, String key) {
if (object.containsKey(key) && !object.isNull(key)) {
return object.getString(key);
@@ -97,11 +103,11 @@ public abstract class JsonGeocoder implements Geocoder {
}
}
- if (Main.getInjector() != null) {
- Main.getInjector().getInstance(StatisticsManager.class).registerGeocoderRequest();
+ if (statisticsManager != null) {
+ statisticsManager.registerGeocoderRequest();
}
- Invocation.Builder request = Context.getClient().target(String.format(url, latitude, longitude)).request();
+ var request = client.target(String.format(url, latitude, longitude)).request();
if (callback != null) {
request.async().get(new InvocationCallback<JsonObject>() {
diff --git a/src/main/java/org/traccar/geocoder/LocationIqGeocoder.java b/src/main/java/org/traccar/geocoder/LocationIqGeocoder.java
new file mode 100644
index 000000000..f2ffe02d6
--- /dev/null
+++ b/src/main/java/org/traccar/geocoder/LocationIqGeocoder.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.geocoder;
+
+import javax.ws.rs.client.Client;
+
+public class LocationIqGeocoder extends NominatimGeocoder {
+
+ private static final String DEFAULT_URL = "https://us1.locationiq.com/v1/reverse.php";
+
+ public LocationIqGeocoder(
+ Client client, String url, String key, String language, int cacheSize, AddressFormat addressFormat) {
+ super(client, url != null ? url : DEFAULT_URL, key, language, cacheSize, addressFormat);
+ }
+
+}
diff --git a/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java b/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java
index 8dc3f76f0..3f2554c6e 100644
--- a/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java
@@ -18,6 +18,7 @@ package org.traccar.geocoder;
import javax.json.JsonArray;
import javax.json.JsonObject;
+import javax.ws.rs.client.Client;
public class MapQuestGeocoder extends JsonGeocoder {
@@ -29,8 +30,8 @@ public class MapQuestGeocoder extends JsonGeocoder {
return url;
}
- public MapQuestGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) {
- super(formatUrl(url, key), cacheSize, addressFormat);
+ public MapQuestGeocoder(Client client, String url, String key, int cacheSize, AddressFormat addressFormat) {
+ super(client, formatUrl(url, key), cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geocoder/MapTilerGeocoder.java b/src/main/java/org/traccar/geocoder/MapTilerGeocoder.java
index 6b688a6e8..203f5f99b 100644
--- a/src/main/java/org/traccar/geocoder/MapTilerGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/MapTilerGeocoder.java
@@ -17,11 +17,12 @@ package org.traccar.geocoder;
import javax.json.JsonArray;
import javax.json.JsonObject;
+import javax.ws.rs.client.Client;
public class MapTilerGeocoder extends JsonGeocoder {
- public MapTilerGeocoder(String key, int cacheSize, AddressFormat addressFormat) {
- super("https://api.maptiler.com/geocoding/%2$f,%1$f.json?key=" + key, cacheSize, addressFormat);
+ public MapTilerGeocoder(Client client, String key, int cacheSize, AddressFormat addressFormat) {
+ super(client, "https://api.maptiler.com/geocoding/%2$f,%1$f.json?key=" + key, cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geocoder/MapboxGeocoder.java b/src/main/java/org/traccar/geocoder/MapboxGeocoder.java
index 9b987c9d8..72bfb53f5 100644
--- a/src/main/java/org/traccar/geocoder/MapboxGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/MapboxGeocoder.java
@@ -18,6 +18,7 @@ package org.traccar.geocoder;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.json.JsonString;
+import javax.ws.rs.client.Client;
public class MapboxGeocoder extends JsonGeocoder {
@@ -25,8 +26,8 @@ public class MapboxGeocoder extends JsonGeocoder {
return "https://api.mapbox.com/geocoding/v5/mapbox.places/%2$f,%1$f.json?access_token=" + key;
}
- public MapboxGeocoder(String key, int cacheSize, AddressFormat addressFormat) {
- super(formatUrl(key), cacheSize, addressFormat);
+ public MapboxGeocoder(Client client, String key, int cacheSize, AddressFormat addressFormat) {
+ super(client, formatUrl(key), cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geocoder/MapmyIndiaGeocoder.java b/src/main/java/org/traccar/geocoder/MapmyIndiaGeocoder.java
index 2b70708a1..dea295cca 100644
--- a/src/main/java/org/traccar/geocoder/MapmyIndiaGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/MapmyIndiaGeocoder.java
@@ -17,11 +17,12 @@ package org.traccar.geocoder;
import javax.json.JsonArray;
import javax.json.JsonObject;
+import javax.ws.rs.client.Client;
public class MapmyIndiaGeocoder extends JsonGeocoder {
- public MapmyIndiaGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) {
- super(url + "/" + key + "/rev_geocode?lat=%f&lng=%f", cacheSize, addressFormat);
+ public MapmyIndiaGeocoder(Client client, String url, String key, int cacheSize, AddressFormat addressFormat) {
+ super(client, url + "/" + key + "/rev_geocode?lat=%f&lng=%f", cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geocoder/NominatimGeocoder.java b/src/main/java/org/traccar/geocoder/NominatimGeocoder.java
index 8db25bf15..b731549f7 100644
--- a/src/main/java/org/traccar/geocoder/NominatimGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/NominatimGeocoder.java
@@ -16,6 +16,7 @@
package org.traccar.geocoder;
import javax.json.JsonObject;
+import javax.ws.rs.client.Client;
public class NominatimGeocoder extends JsonGeocoder {
@@ -33,8 +34,9 @@ public class NominatimGeocoder extends JsonGeocoder {
return url;
}
- public NominatimGeocoder(String url, String key, String language, int cacheSize, AddressFormat addressFormat) {
- super(formatUrl(url, key, language), cacheSize, addressFormat);
+ public NominatimGeocoder(
+ Client client, String url, String key, String language, int cacheSize, AddressFormat addressFormat) {
+ super(client, formatUrl(url, key, language), cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geocoder/OpenCageGeocoder.java b/src/main/java/org/traccar/geocoder/OpenCageGeocoder.java
index 56161e52c..fb61440aa 100644
--- a/src/main/java/org/traccar/geocoder/OpenCageGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/OpenCageGeocoder.java
@@ -18,19 +18,24 @@ package org.traccar.geocoder;
import javax.json.JsonArray;
import javax.json.JsonObject;
+import javax.ws.rs.client.Client;
public class OpenCageGeocoder extends JsonGeocoder {
- private static String formatUrl(String url, String key) {
+ private static String formatUrl(String url, String key, String language) {
if (url == null) {
url = "https://api.opencagedata.com/geocode/v1";
}
url += "/json?q=%f,%f&no_annotations=1&key=" + key;
+ if (language != null) {
+ url += "&language=" + language;
+ }
return url;
}
- public OpenCageGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) {
- super(formatUrl(url, key), cacheSize, addressFormat);
+ public OpenCageGeocoder(
+ Client client, String url, String key, String language, int cacheSize, AddressFormat addressFormat) {
+ super(client, formatUrl(url, key, language), cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geocoder/PositionStackGeocoder.java b/src/main/java/org/traccar/geocoder/PositionStackGeocoder.java
index 2674a68ca..9778d9eda 100644
--- a/src/main/java/org/traccar/geocoder/PositionStackGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/PositionStackGeocoder.java
@@ -17,6 +17,7 @@ package org.traccar.geocoder;
import javax.json.JsonArray;
import javax.json.JsonObject;
+import javax.ws.rs.client.Client;
public class PositionStackGeocoder extends JsonGeocoder {
@@ -24,8 +25,8 @@ public class PositionStackGeocoder extends JsonGeocoder {
return "http://api.positionstack.com/v1/reverse?access_key=" + key + "&query=%f,%f";
}
- public PositionStackGeocoder(String key, int cacheSize, AddressFormat addressFormat) {
- super(formatUrl(key), cacheSize, addressFormat);
+ public PositionStackGeocoder(Client client, String key, int cacheSize, AddressFormat addressFormat) {
+ super(client, formatUrl(key), cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geocoder/TestGeocoder.java b/src/main/java/org/traccar/geocoder/TestGeocoder.java
new file mode 100644
index 000000000..259f13c6c
--- /dev/null
+++ b/src/main/java/org/traccar/geocoder/TestGeocoder.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.geocoder;
+
+import org.traccar.database.StatisticsManager;
+
+public class TestGeocoder implements Geocoder {
+
+ @Override
+ public void setStatisticsManager(StatisticsManager statisticsManager) {
+ }
+
+ @Override
+ public String getAddress(double latitude, double longitude, ReverseGeocoderCallback callback) {
+ String address = String.format("Location %f, %f", latitude, longitude);
+ if (callback != null) {
+ callback.onSuccess(address);
+ return null;
+ }
+ return address;
+ }
+
+}
diff --git a/src/main/java/org/traccar/geocoder/TomTomGeocoder.java b/src/main/java/org/traccar/geocoder/TomTomGeocoder.java
index 232b24396..9bb36efc2 100644
--- a/src/main/java/org/traccar/geocoder/TomTomGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/TomTomGeocoder.java
@@ -17,6 +17,7 @@ package org.traccar.geocoder;
import javax.json.JsonArray;
import javax.json.JsonObject;
+import javax.ws.rs.client.Client;
public class TomTomGeocoder extends JsonGeocoder {
@@ -28,8 +29,8 @@ public class TomTomGeocoder extends JsonGeocoder {
return url;
}
- public TomTomGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) {
- super(formatUrl(url, key), cacheSize, addressFormat);
+ public TomTomGeocoder(Client client, String url, String key, int cacheSize, AddressFormat addressFormat) {
+ super(client, formatUrl(url, key), cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geofence/GeofenceCircle.java b/src/main/java/org/traccar/geofence/GeofenceCircle.java
index 5d566f84e..59feb1730 100644
--- a/src/main/java/org/traccar/geofence/GeofenceCircle.java
+++ b/src/main/java/org/traccar/geofence/GeofenceCircle.java
@@ -18,7 +18,9 @@ package org.traccar.geofence;
import java.text.DecimalFormat;
import java.text.ParseException;
+import org.traccar.config.Config;
import org.traccar.helper.DistanceCalculator;
+import org.traccar.model.Geofence;
public class GeofenceCircle extends GeofenceGeometry {
@@ -44,7 +46,7 @@ public class GeofenceCircle extends GeofenceGeometry {
}
@Override
- public boolean containsPoint(double latitude, double longitude) {
+ public boolean containsPoint(Config config, Geofence geofence, double latitude, double longitude) {
return distanceFromCenter(latitude, longitude) <= radius;
}
diff --git a/src/main/java/org/traccar/geofence/GeofenceGeometry.java b/src/main/java/org/traccar/geofence/GeofenceGeometry.java
index 2c45c22af..fadabab1c 100644
--- a/src/main/java/org/traccar/geofence/GeofenceGeometry.java
+++ b/src/main/java/org/traccar/geofence/GeofenceGeometry.java
@@ -15,11 +15,14 @@
*/
package org.traccar.geofence;
+import org.traccar.config.Config;
+import org.traccar.model.Geofence;
+
import java.text.ParseException;
public abstract class GeofenceGeometry {
- public abstract boolean containsPoint(double latitude, double longitude);
+ public abstract boolean containsPoint(Config config, Geofence geofence, double latitude, double longitude);
public abstract double calculateArea();
diff --git a/src/main/java/org/traccar/geofence/GeofencePolygon.java b/src/main/java/org/traccar/geofence/GeofencePolygon.java
index cd2cbf16a..13f6658ef 100644
--- a/src/main/java/org/traccar/geofence/GeofencePolygon.java
+++ b/src/main/java/org/traccar/geofence/GeofencePolygon.java
@@ -19,6 +19,8 @@ import org.locationtech.spatial4j.context.SpatialContext;
import org.locationtech.spatial4j.context.jts.JtsSpatialContextFactory;
import org.locationtech.spatial4j.shape.ShapeFactory;
import org.locationtech.spatial4j.shape.jts.JtsShapeFactory;
+import org.traccar.config.Config;
+import org.traccar.model.Geofence;
import java.text.ParseException;
import java.util.ArrayList;
@@ -95,7 +97,7 @@ public class GeofencePolygon extends GeofenceGeometry {
}
@Override
- public boolean containsPoint(double latitude, double longitude) {
+ public boolean containsPoint(Config config, Geofence geofence, double latitude, double longitude) {
int polyCorners = coordinates.size();
int i;
diff --git a/src/main/java/org/traccar/geofence/GeofencePolyline.java b/src/main/java/org/traccar/geofence/GeofencePolyline.java
index 370bf99bb..d9c280ae4 100644
--- a/src/main/java/org/traccar/geofence/GeofencePolyline.java
+++ b/src/main/java/org/traccar/geofence/GeofencePolyline.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,23 +19,28 @@ package org.traccar.geofence;
import java.text.ParseException;
import java.util.ArrayList;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
import org.traccar.helper.DistanceCalculator;
+import org.traccar.model.Geofence;
public class GeofencePolyline extends GeofenceGeometry {
private ArrayList<Coordinate> coordinates;
- private double distance;
public GeofencePolyline() {
}
- public GeofencePolyline(String wkt, double distance) throws ParseException {
+ public GeofencePolyline(String wkt) throws ParseException {
fromWkt(wkt);
- this.distance = distance;
}
@Override
- public boolean containsPoint(double latitude, double longitude) {
+ public boolean containsPoint(Config config, Geofence geofence, double latitude, double longitude) {
+ double distance = geofence.getDouble("polylineDistance");
+ if (distance == 0) {
+ distance = config.getDouble(Keys.GEOFENCE_POLYLINE_DISTANCE);
+ }
for (int i = 1; i < coordinates.size(); i++) {
if (DistanceCalculator.distanceToLine(
latitude, longitude, coordinates.get(i - 1).getLat(), coordinates.get(i - 1).getLon(),
@@ -56,9 +61,9 @@ public class GeofencePolyline extends GeofenceGeometry {
StringBuilder buf = new StringBuilder();
buf.append("LINESTRING (");
for (Coordinate coordinate : coordinates) {
- buf.append(String.valueOf(coordinate.getLat()));
+ buf.append(coordinate.getLat());
buf.append(" ");
- buf.append(String.valueOf(coordinate.getLon()));
+ buf.append(coordinate.getLon());
buf.append(", ");
}
return buf.substring(0, buf.length() - 2) + ")";
@@ -105,8 +110,4 @@ public class GeofencePolyline extends GeofenceGeometry {
}
- public void setDistance(double distance) {
- this.distance = distance;
- }
-
}
diff --git a/src/main/java/org/traccar/geolocation/GoogleGeolocationProvider.java b/src/main/java/org/traccar/geolocation/GoogleGeolocationProvider.java
index 5901b47cd..8f0f3b704 100644
--- a/src/main/java/org/traccar/geolocation/GoogleGeolocationProvider.java
+++ b/src/main/java/org/traccar/geolocation/GoogleGeolocationProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,12 +15,14 @@
*/
package org.traccar.geolocation;
+import javax.ws.rs.client.Client;
+
public class GoogleGeolocationProvider extends UniversalGeolocationProvider {
private static final String URL = "https://www.googleapis.com/geolocation/v1/geolocate";
- public GoogleGeolocationProvider(String key) {
- super(URL, key);
+ public GoogleGeolocationProvider(Client client, String key) {
+ super(client, URL, key);
}
}
diff --git a/src/main/java/org/traccar/geolocation/MozillaGeolocationProvider.java b/src/main/java/org/traccar/geolocation/MozillaGeolocationProvider.java
index c6a73a52b..3b4ba4e1f 100644
--- a/src/main/java/org/traccar/geolocation/MozillaGeolocationProvider.java
+++ b/src/main/java/org/traccar/geolocation/MozillaGeolocationProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,12 +15,14 @@
*/
package org.traccar.geolocation;
+import javax.ws.rs.client.Client;
+
public class MozillaGeolocationProvider extends UniversalGeolocationProvider {
private static final String URL = "https://location.services.mozilla.com/v1/geolocate";
- public MozillaGeolocationProvider(String key) {
- super(URL, key != null ? key : "test");
+ public MozillaGeolocationProvider(Client client, String key) {
+ super(client, URL, key != null ? key : "test");
}
}
diff --git a/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java b/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java
index 2535970d3..82fcf42ab 100644
--- a/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java
+++ b/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,18 +15,20 @@
*/
package org.traccar.geolocation;
-import org.traccar.Context;
import org.traccar.model.CellTower;
import org.traccar.model.Network;
import javax.json.JsonObject;
+import javax.ws.rs.client.Client;
import javax.ws.rs.client.InvocationCallback;
public class OpenCellIdGeolocationProvider implements GeolocationProvider {
- private String url;
+ private final Client client;
+ private final String url;
- public OpenCellIdGeolocationProvider(String url, String key) {
+ public OpenCellIdGeolocationProvider(Client client, String url, String key) {
+ this.client = client;
if (url == null) {
url = "http://opencellid.org/cell/get";
}
@@ -41,7 +43,7 @@ public class OpenCellIdGeolocationProvider implements GeolocationProvider {
String request = String.format(url, cellTower.getMobileCountryCode(), cellTower.getMobileNetworkCode(),
cellTower.getLocationAreaCode(), cellTower.getCellId());
- Context.getClient().target(request).request().async().get(new InvocationCallback<JsonObject>() {
+ client.target(request).request().async().get(new InvocationCallback<JsonObject>() {
@Override
public void completed(JsonObject json) {
if (json.containsKey("lat") && json.containsKey("lon")) {
diff --git a/src/main/java/org/traccar/geolocation/UniversalGeolocationProvider.java b/src/main/java/org/traccar/geolocation/UniversalGeolocationProvider.java
index f71620d8a..7a3f71ee1 100644
--- a/src/main/java/org/traccar/geolocation/UniversalGeolocationProvider.java
+++ b/src/main/java/org/traccar/geolocation/UniversalGeolocationProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,26 +15,26 @@
*/
package org.traccar.geolocation;
-import org.traccar.Context;
import org.traccar.model.Network;
import javax.json.JsonObject;
-import javax.ws.rs.client.AsyncInvoker;
+import javax.ws.rs.client.Client;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.InvocationCallback;
public class UniversalGeolocationProvider implements GeolocationProvider {
- private String url;
+ private final Client client;
+ private final String url;
- public UniversalGeolocationProvider(String url, String key) {
+ public UniversalGeolocationProvider(Client client, String url, String key) {
+ this.client = client;
this.url = url + "?key=" + key;
}
@Override
public void getLocation(Network network, final LocationProviderCallback callback) {
- AsyncInvoker invoker = Context.getClient().target(url).request().async();
- invoker.post(Entity.json(network), new InvocationCallback<JsonObject>() {
+ client.target(url).request().async().post(Entity.json(network), new InvocationCallback<JsonObject>() {
@Override
public void completed(JsonObject json) {
if (json.containsKey("error")) {
diff --git a/src/main/java/org/traccar/geolocation/UnwiredGeolocationProvider.java b/src/main/java/org/traccar/geolocation/UnwiredGeolocationProvider.java
index 963bcb688..14893b6a3 100644
--- a/src/main/java/org/traccar/geolocation/UnwiredGeolocationProvider.java
+++ b/src/main/java/org/traccar/geolocation/UnwiredGeolocationProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,22 +19,23 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
-import org.traccar.Context;
import org.traccar.model.CellTower;
import org.traccar.model.Network;
import org.traccar.model.WifiAccessPoint;
import javax.json.JsonObject;
+import javax.ws.rs.client.Client;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.InvocationCallback;
import java.util.Collection;
public class UnwiredGeolocationProvider implements GeolocationProvider {
- private String url;
- private String key;
+ private final Client client;
+ private final String url;
+ private final String key;
- private ObjectMapper objectMapper;
+ private final ObjectMapper objectMapper;
private abstract static class NetworkMixIn {
@JsonProperty("mcc")
@@ -73,7 +74,8 @@ public class UnwiredGeolocationProvider implements GeolocationProvider {
abstract Integer getSignalStrength();
}
- public UnwiredGeolocationProvider(String url, String key) {
+ public UnwiredGeolocationProvider(Client client, String url, String key) {
+ this.client = client;
this.url = url;
this.key = key;
@@ -88,7 +90,7 @@ public class UnwiredGeolocationProvider implements GeolocationProvider {
ObjectNode json = objectMapper.valueToTree(network);
json.put("token", key);
- Context.getClient().target(url).request().async().post(Entity.json(json), new InvocationCallback<JsonObject>() {
+ client.target(url).request().async().post(Entity.json(json), new InvocationCallback<JsonObject>() {
@Override
public void completed(JsonObject json) {
if (json.getString("status").equals("error")) {
diff --git a/src/main/java/org/traccar/handler/ComputedAttributesHandler.java b/src/main/java/org/traccar/handler/ComputedAttributesHandler.java
index 153da29b9..c9f1f63d7 100644
--- a/src/main/java/org/traccar/handler/ComputedAttributesHandler.java
+++ b/src/main/java/org/traccar/handler/ComputedAttributesHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -34,28 +34,29 @@ import org.slf4j.LoggerFactory;
import org.traccar.BaseDataHandler;
import org.traccar.config.Config;
import org.traccar.config.Keys;
-import org.traccar.database.AttributesManager;
-import org.traccar.database.IdentityManager;
import org.traccar.model.Attribute;
import org.traccar.model.Device;
import org.traccar.model.Position;
+import org.traccar.session.cache.CacheManager;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton
@ChannelHandler.Sharable
public class ComputedAttributesHandler extends BaseDataHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(ComputedAttributesHandler.class);
- private final IdentityManager identityManager;
- private final AttributesManager attributesManager;
+ private final CacheManager cacheManager;
private final JexlEngine engine;
private final boolean includeDeviceAttributes;
- public ComputedAttributesHandler(
- Config config, IdentityManager identityManager, AttributesManager attributesManager) {
- this.identityManager = identityManager;
- this.attributesManager = attributesManager;
+ @Inject
+ public ComputedAttributesHandler(Config config, CacheManager cacheManager) {
+ this.cacheManager = cacheManager;
engine = new JexlEngine();
engine.setStrict(true);
engine.setFunctions(Collections.singletonMap("math", Math.class));
@@ -65,7 +66,7 @@ public class ComputedAttributesHandler extends BaseDataHandler {
private MapContext prepareContext(Position position) {
MapContext result = new MapContext();
if (includeDeviceAttributes) {
- Device device = identityManager.getById(position.getDeviceId());
+ Device device = cacheManager.getObject(Device.class, position.getDeviceId());
if (device != null) {
for (Object key : device.getAttributes().keySet()) {
result.set((String) key, device.getAttributes().get(key));
@@ -104,8 +105,7 @@ public class ComputedAttributesHandler extends BaseDataHandler {
@Override
protected Position handlePosition(Position position) {
- Collection<Attribute> attributes = attributesManager.getItems(
- attributesManager.getAllDeviceItems(position.getDeviceId()));
+ Collection<Attribute> attributes = cacheManager.getDeviceObjects(position.getDeviceId(), Attribute.class);
for (Attribute attribute : attributes) {
if (attribute.getAttribute() != null) {
Object result = null;
diff --git a/src/main/java/org/traccar/handler/CopyAttributesHandler.java b/src/main/java/org/traccar/handler/CopyAttributesHandler.java
index f386116b0..e5c9bc29a 100644
--- a/src/main/java/org/traccar/handler/CopyAttributesHandler.java
+++ b/src/main/java/org/traccar/handler/CopyAttributesHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2016 - 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,27 +18,39 @@ package org.traccar.handler;
import io.netty.channel.ChannelHandler;
import org.traccar.BaseDataHandler;
-import org.traccar.database.IdentityManager;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.helper.model.AttributeUtil;
import org.traccar.model.Position;
+import org.traccar.session.cache.CacheManager;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton
@ChannelHandler.Sharable
public class CopyAttributesHandler extends BaseDataHandler {
- private IdentityManager identityManager;
+ private final boolean enabled;
+ private final CacheManager cacheManager;
- public CopyAttributesHandler(IdentityManager identityManager) {
- this.identityManager = identityManager;
+ @Inject
+ public CopyAttributesHandler(Config config, CacheManager cacheManager) {
+ enabled = config.getBoolean(Keys.PROCESSING_COPY_ATTRIBUTES_ENABLE);
+ this.cacheManager = cacheManager;
}
@Override
protected Position handlePosition(Position position) {
- String attributesString = identityManager.lookupAttributeString(
- position.getDeviceId(), "processing.copyAttributes", "", false, true);
- Position last = identityManager.getLastPosition(position.getDeviceId());
- if (last != null) {
- for (String attribute : attributesString.split("[ ,]")) {
- if (last.getAttributes().containsKey(attribute) && !position.getAttributes().containsKey(attribute)) {
- position.getAttributes().put(attribute, last.getAttributes().get(attribute));
+ if (enabled) {
+ String attributesString = AttributeUtil.lookup(
+ cacheManager, Keys.PROCESSING_COPY_ATTRIBUTES, position.getDeviceId());
+ Position last = cacheManager.getPosition(position.getDeviceId());
+ if (last != null && attributesString != null) {
+ for (String attribute : attributesString.split("[ ,]")) {
+ if (last.hasAttribute(attribute) && !position.hasAttribute(attribute)) {
+ position.getAttributes().put(attribute, last.getAttributes().get(attribute));
+ }
}
}
}
diff --git a/src/main/java/org/traccar/handler/DefaultDataHandler.java b/src/main/java/org/traccar/handler/DefaultDataHandler.java
index 9d8ea044d..89255a5fe 100644
--- a/src/main/java/org/traccar/handler/DefaultDataHandler.java
+++ b/src/main/java/org/traccar/handler/DefaultDataHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,25 +19,32 @@ import io.netty.channel.ChannelHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.BaseDataHandler;
-import org.traccar.database.DataManager;
import org.traccar.model.Position;
+import org.traccar.storage.Storage;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Request;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton
@ChannelHandler.Sharable
public class DefaultDataHandler extends BaseDataHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultDataHandler.class);
- private final DataManager dataManager;
+ private final Storage storage;
- public DefaultDataHandler(DataManager dataManager) {
- this.dataManager = dataManager;
+ @Inject
+ public DefaultDataHandler(Storage storage) {
+ this.storage = storage;
}
@Override
protected Position handlePosition(Position position) {
try {
- dataManager.addObject(position);
+ position.setId(storage.addObject(position, new Request(new Columns.Exclude("id"))));
} catch (Exception error) {
LOGGER.warn("Failed to store position", error);
}
diff --git a/src/main/java/org/traccar/handler/DistanceHandler.java b/src/main/java/org/traccar/handler/DistanceHandler.java
index 1e7e444f6..30dc9ff2b 100644
--- a/src/main/java/org/traccar/handler/DistanceHandler.java
+++ b/src/main/java/org/traccar/handler/DistanceHandler.java
@@ -1,6 +1,6 @@
/*
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2015 Amila Silva
- * Copyright 2016 - 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,24 +20,28 @@ import io.netty.channel.ChannelHandler;
import org.traccar.BaseDataHandler;
import org.traccar.config.Config;
import org.traccar.config.Keys;
-import org.traccar.database.IdentityManager;
import org.traccar.helper.DistanceCalculator;
import org.traccar.model.Position;
+import org.traccar.session.cache.CacheManager;
+import javax.inject.Inject;
+import javax.inject.Singleton;
import java.math.BigDecimal;
import java.math.RoundingMode;
+@Singleton
@ChannelHandler.Sharable
public class DistanceHandler extends BaseDataHandler {
- private final IdentityManager identityManager;
+ private final CacheManager cacheManager;
private final boolean filter;
private final int coordinatesMinError;
private final int coordinatesMaxError;
- public DistanceHandler(Config config, IdentityManager identityManager) {
- this.identityManager = identityManager;
+ @Inject
+ public DistanceHandler(Config config, CacheManager cacheManager) {
+ this.cacheManager = cacheManager;
this.filter = config.getBoolean(Keys.COORDINATES_FILTER);
this.coordinatesMinError = config.getInteger(Keys.COORDINATES_MIN_ERROR);
this.coordinatesMaxError = config.getInteger(Keys.COORDINATES_MAX_ERROR);
@@ -47,15 +51,15 @@ public class DistanceHandler extends BaseDataHandler {
protected Position handlePosition(Position position) {
double distance = 0.0;
- if (position.getAttributes().containsKey(Position.KEY_DISTANCE)) {
+ if (position.hasAttribute(Position.KEY_DISTANCE)) {
distance = position.getDouble(Position.KEY_DISTANCE);
}
double totalDistance = 0.0;
- Position last = identityManager != null ? identityManager.getLastPosition(position.getDeviceId()) : null;
+ Position last = cacheManager.getPosition(position.getDeviceId());
if (last != null) {
totalDistance = last.getDouble(Position.KEY_TOTAL_DISTANCE);
- if (!position.getAttributes().containsKey(Position.KEY_DISTANCE)) {
+ if (!position.hasAttribute(Position.KEY_DISTANCE)) {
distance = DistanceCalculator.distance(
position.getLatitude(), position.getLongitude(),
last.getLatitude(), last.getLongitude());
diff --git a/src/main/java/org/traccar/handler/EngineHoursHandler.java b/src/main/java/org/traccar/handler/EngineHoursHandler.java
index 92da84e6b..c10fe9064 100644
--- a/src/main/java/org/traccar/handler/EngineHoursHandler.java
+++ b/src/main/java/org/traccar/handler/EngineHoursHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,22 +18,27 @@ package org.traccar.handler;
import io.netty.channel.ChannelHandler;
import org.traccar.BaseDataHandler;
-import org.traccar.database.IdentityManager;
import org.traccar.model.Position;
+import org.traccar.session.cache.CacheManager;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton
@ChannelHandler.Sharable
public class EngineHoursHandler extends BaseDataHandler {
- private final IdentityManager identityManager;
+ private final CacheManager cacheManager;
- public EngineHoursHandler(IdentityManager identityManager) {
- this.identityManager = identityManager;
+ @Inject
+ public EngineHoursHandler(CacheManager cacheManager) {
+ this.cacheManager = cacheManager;
}
@Override
protected Position handlePosition(Position position) {
- if (!position.getAttributes().containsKey(Position.KEY_HOURS)) {
- Position last = identityManager.getLastPosition(position.getDeviceId());
+ if (!position.hasAttribute(Position.KEY_HOURS)) {
+ Position last = cacheManager.getPosition(position.getDeviceId());
if (last != null) {
long hours = last.getLong(Position.KEY_HOURS);
if (last.getBoolean(Position.KEY_IGNITION) && position.getBoolean(Position.KEY_IGNITION)) {
diff --git a/src/main/java/org/traccar/handler/FilterHandler.java b/src/main/java/org/traccar/handler/FilterHandler.java
index 7cd9153c1..994276bb6 100644
--- a/src/main/java/org/traccar/handler/FilterHandler.java
+++ b/src/main/java/org/traccar/handler/FilterHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,49 +19,83 @@ import io.netty.channel.ChannelHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.BaseDataHandler;
-import org.traccar.Context;
import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.helper.UnitsConverter;
+import org.traccar.helper.model.AttributeUtil;
+import org.traccar.model.Device;
import org.traccar.model.Position;
-
+import org.traccar.session.cache.CacheManager;
+import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Order;
+import org.traccar.storage.query.Request;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.util.Date;
+
+@Singleton
@ChannelHandler.Sharable
public class FilterHandler extends BaseDataHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(FilterHandler.class);
- private boolean filterInvalid;
- private boolean filterZero;
- private boolean filterDuplicate;
- private long filterFuture;
- private boolean filterApproximate;
- private int filterAccuracy;
- private boolean filterStatic;
- private int filterDistance;
- private int filterMaxSpeed;
- private long filterMinPeriod;
- private long skipLimit;
- private boolean skipAttributes;
-
- public FilterHandler(Config config) {
+ private final boolean enabled;
+ private final boolean filterInvalid;
+ private final boolean filterZero;
+ private final boolean filterDuplicate;
+ private final long filterFuture;
+ private final long filterPast;
+ private final boolean filterApproximate;
+ private final int filterAccuracy;
+ private final boolean filterStatic;
+ private final int filterDistance;
+ private final int filterMaxSpeed;
+ private final long filterMinPeriod;
+ private final boolean filterRelative;
+ private final long skipLimit;
+ private final boolean skipAttributes;
+
+ private final CacheManager cacheManager;
+ private final Storage storage;
+
+ @Inject
+ public FilterHandler(Config config, CacheManager cacheManager, Storage storage) {
+ enabled = config.getBoolean(Keys.FILTER_ENABLE);
filterInvalid = config.getBoolean(Keys.FILTER_INVALID);
filterZero = config.getBoolean(Keys.FILTER_ZERO);
filterDuplicate = config.getBoolean(Keys.FILTER_DUPLICATE);
filterFuture = config.getLong(Keys.FILTER_FUTURE) * 1000;
+ filterPast = config.getLong(Keys.FILTER_PAST) * 1000;
filterAccuracy = config.getInteger(Keys.FILTER_ACCURACY);
filterApproximate = config.getBoolean(Keys.FILTER_APPROXIMATE);
filterStatic = config.getBoolean(Keys.FILTER_STATIC);
filterDistance = config.getInteger(Keys.FILTER_DISTANCE);
filterMaxSpeed = config.getInteger(Keys.FILTER_MAX_SPEED);
- filterMinPeriod = config.getInteger(Keys.FILTER_MIN_PERIOD) * 1000;
+ filterMinPeriod = config.getInteger(Keys.FILTER_MIN_PERIOD) * 1000L;
+ 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;
+ }
+
+ private Position getPrecedingPosition(long deviceId, Date date) throws StorageException {
+ return storage.getObject(Position.class, new Request(
+ new Columns.All(),
+ new Condition.And(
+ new Condition.Equals("deviceId", deviceId),
+ new Condition.Compare("fixTime", "<=", "time", date)),
+ new Order("fixTime", true, 1)));
}
private boolean filterInvalid(Position position) {
return filterInvalid && (!position.getValid()
- || position.getLatitude() > 90 || position.getLongitude() > 180
- || position.getLatitude() < -90 || position.getLongitude() < -180);
+ || position.getLatitude() > 90 || position.getLongitude() > 180
+ || position.getLatitude() < -90 || position.getLongitude() < -180);
}
private boolean filterZero(Position position) {
@@ -71,7 +105,7 @@ public class FilterHandler extends BaseDataHandler {
private boolean filterDuplicate(Position position, Position last) {
if (filterDuplicate && last != null && position.getFixTime().equals(last.getFixTime())) {
for (String key : position.getAttributes().keySet()) {
- if (!last.getAttributes().containsKey(key)) {
+ if (!last.hasAttribute(key)) {
return false;
}
}
@@ -84,6 +118,10 @@ public class FilterHandler extends BaseDataHandler {
return filterFuture != 0 && position.getFixTime().getTime() > System.currentTimeMillis() + filterFuture;
}
+ private boolean filterPast(Position position) {
+ return filterPast != 0 && position.getFixTime().getTime() < System.currentTimeMillis() - filterPast;
+ }
+
private boolean filterAccuracy(Position position) {
return filterAccuracy != 0 && position.getAccuracy() > filterAccuracy;
}
@@ -129,10 +167,9 @@ public class FilterHandler extends BaseDataHandler {
private boolean skipAttributes(Position position) {
if (skipAttributes) {
- String attributesString = Context.getIdentityManager().lookupAttributeString(
- position.getDeviceId(), "filter.skipAttributes", "", false, true);
- for (String attribute : attributesString.split("[ ,]")) {
- if (position.getAttributes().containsKey(attribute)) {
+ String string = AttributeUtil.lookup(cacheManager, Keys.FILTER_SKIP_ATTRIBUTES, position.getDeviceId());
+ for (String attribute : string.split("[ ,]")) {
+ if (position.hasAttribute(attribute)) {
return true;
}
}
@@ -144,51 +181,61 @@ public class FilterHandler extends BaseDataHandler {
StringBuilder filterType = new StringBuilder();
- Position last = null;
- if (Context.getIdentityManager() != null) {
- last = Context.getIdentityManager().getLastPosition(position.getDeviceId());
- }
-
+ // filter out invalid data
if (filterInvalid(position)) {
filterType.append("Invalid ");
}
if (filterZero(position)) {
filterType.append("Zero ");
}
- if (filterDuplicate(position, last) && !skipLimit(position, last) && !skipAttributes(position)) {
- filterType.append("Duplicate ");
- }
if (filterFuture(position)) {
filterType.append("Future ");
}
+ if (filterPast(position)) {
+ filterType.append("Past ");
+ }
if (filterAccuracy(position)) {
filterType.append("Accuracy ");
}
if (filterApproximate(position)) {
filterType.append("Approximate ");
}
- if (filterStatic(position) && !skipLimit(position, last) && !skipAttributes(position)) {
- filterType.append("Static ");
- }
- if (filterDistance(position, last) && !skipLimit(position, last) && !skipAttributes(position)) {
- filterType.append("Distance ");
- }
- if (filterMaxSpeed(position, last)) {
- filterType.append("MaxSpeed ");
- }
- if (filterMinPeriod(position, last)) {
- filterType.append("MinPeriod ");
+
+ // filter out excessive data
+ long deviceId = position.getDeviceId();
+ if (filterDuplicate || filterStatic || filterDistance > 0 || filterMaxSpeed > 0 || filterMinPeriod > 0) {
+ Position preceding = null;
+ if (filterRelative) {
+ try {
+ Date newFixTime = position.getFixTime();
+ preceding = getPrecedingPosition(deviceId, newFixTime);
+ } catch (StorageException e) {
+ LOGGER.warn("Error retrieving preceding position; fallbacking to last received position.", e);
+ preceding = cacheManager.getPosition(deviceId);
+ }
+ } else {
+ preceding = cacheManager.getPosition(deviceId);
+ }
+ if (filterDuplicate(position, preceding) && !skipLimit(position, preceding) && !skipAttributes(position)) {
+ filterType.append("Duplicate ");
+ }
+ if (filterStatic(position) && !skipLimit(position, preceding) && !skipAttributes(position)) {
+ filterType.append("Static ");
+ }
+ if (filterDistance(position, preceding) && !skipLimit(position, preceding) && !skipAttributes(position)) {
+ filterType.append("Distance ");
+ }
+ if (filterMaxSpeed(position, preceding)) {
+ filterType.append("MaxSpeed ");
+ }
+ if (filterMinPeriod(position, preceding)) {
+ filterType.append("MinPeriod ");
+ }
}
if (filterType.length() > 0) {
-
- StringBuilder message = new StringBuilder();
- message.append("Position filtered by ");
- message.append(filterType.toString());
- message.append("filters from device: ");
- message.append(Context.getIdentityManager().getById(position.getDeviceId()).getUniqueId());
-
- LOGGER.info(message.toString());
+ String uniqueId = cacheManager.getObject(Device.class, deviceId).getUniqueId();
+ LOGGER.info("Position filtered by {}filters from device: {}", filterType, uniqueId);
return true;
}
@@ -197,7 +244,7 @@ public class FilterHandler extends BaseDataHandler {
@Override
protected Position handlePosition(Position position) {
- if (filter(position)) {
+ if (enabled && filter(position)) {
return null;
}
return position;
diff --git a/src/main/java/org/traccar/handler/GeocoderHandler.java b/src/main/java/org/traccar/handler/GeocoderHandler.java
index 614cf97d6..e4f240a90 100644
--- a/src/main/java/org/traccar/handler/GeocoderHandler.java
+++ b/src/main/java/org/traccar/handler/GeocoderHandler.java
@@ -20,12 +20,11 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.traccar.Context;
import org.traccar.config.Config;
import org.traccar.config.Keys;
-import org.traccar.database.IdentityManager;
import org.traccar.geocoder.Geocoder;
import org.traccar.model.Position;
+import org.traccar.session.cache.CacheManager;
@ChannelHandler.Sharable
public class GeocoderHandler extends ChannelInboundHandlerAdapter {
@@ -33,18 +32,17 @@ public class GeocoderHandler extends ChannelInboundHandlerAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(GeocoderHandler.class);
private final Geocoder geocoder;
- private final IdentityManager identityManager;
+ private final CacheManager cacheManager;
private final boolean ignorePositions;
private final boolean processInvalidPositions;
- private final int geocoderReuseDistance;
+ private final int reuseDistance;
- public GeocoderHandler(
- Config config, Geocoder geocoder, IdentityManager identityManager) {
+ public GeocoderHandler(Config config, Geocoder geocoder, CacheManager cacheManager) {
this.geocoder = geocoder;
- this.identityManager = identityManager;
- ignorePositions = Context.getConfig().getBoolean(Keys.GEOCODER_IGNORE_POSITIONS);
+ this.cacheManager = cacheManager;
+ ignorePositions = config.getBoolean(Keys.GEOCODER_IGNORE_POSITIONS);
processInvalidPositions = config.getBoolean(Keys.GEOCODER_PROCESS_INVALID_POSITIONS);
- geocoderReuseDistance = config.getInteger(Keys.GEOCODER_REUSE_DISTANCE, 0);
+ reuseDistance = config.getInteger(Keys.GEOCODER_REUSE_DISTANCE, 0);
}
@Override
@@ -52,10 +50,10 @@ public class GeocoderHandler extends ChannelInboundHandlerAdapter {
if (message instanceof Position && !ignorePositions) {
final Position position = (Position) message;
if (processInvalidPositions || position.getValid()) {
- if (geocoderReuseDistance != 0) {
- Position lastPosition = identityManager.getLastPosition(position.getDeviceId());
+ if (reuseDistance != 0) {
+ Position lastPosition = cacheManager.getPosition(position.getDeviceId());
if (lastPosition != null && lastPosition.getAddress() != null
- && position.getDouble(Position.KEY_DISTANCE) <= geocoderReuseDistance) {
+ && position.getDouble(Position.KEY_DISTANCE) <= reuseDistance) {
position.setAddress(lastPosition.getAddress());
ctx.fireChannelRead(position);
return;
diff --git a/src/main/java/org/traccar/handler/GeolocationHandler.java b/src/main/java/org/traccar/handler/GeolocationHandler.java
index 0e78322c8..e7389f22d 100644
--- a/src/main/java/org/traccar/handler/GeolocationHandler.java
+++ b/src/main/java/org/traccar/handler/GeolocationHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@ import org.traccar.config.Keys;
import org.traccar.database.StatisticsManager;
import org.traccar.geolocation.GeolocationProvider;
import org.traccar.model.Position;
+import org.traccar.session.cache.CacheManager;
@ChannelHandler.Sharable
public class GeolocationHandler extends ChannelInboundHandlerAdapter {
@@ -32,14 +33,19 @@ public class GeolocationHandler extends ChannelInboundHandlerAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(GeolocationHandler.class);
private final GeolocationProvider geolocationProvider;
+ private final CacheManager cacheManager;
private final StatisticsManager statisticsManager;
private final boolean processInvalidPositions;
+ private final boolean reuse;
public GeolocationHandler(
- Config config, GeolocationProvider geolocationProvider, StatisticsManager statisticsManager) {
+ Config config, GeolocationProvider geolocationProvider, CacheManager cacheManager,
+ StatisticsManager statisticsManager) {
this.geolocationProvider = geolocationProvider;
+ this.cacheManager = cacheManager;
this.statisticsManager = statisticsManager;
- this.processInvalidPositions = config.getBoolean(Keys.GEOLOCATION_PROCESS_INVALID_POSITIONS);
+ processInvalidPositions = config.getBoolean(Keys.GEOLOCATION_PROCESS_INVALID_POSITIONS);
+ reuse = config.getBoolean(Keys.GEOLOCATION_REUSE);
}
@Override
@@ -48,6 +54,17 @@ public class GeolocationHandler extends ChannelInboundHandlerAdapter {
final Position position = (Position) message;
if ((position.getOutdated() || processInvalidPositions && !position.getValid())
&& position.getNetwork() != null) {
+ if (reuse) {
+ Position lastPosition = cacheManager.getPosition(position.getDeviceId());
+ if (lastPosition != null && position.getNetwork().equals(lastPosition.getNetwork())) {
+ updatePosition(
+ position, lastPosition.getLatitude(), lastPosition.getLongitude(),
+ lastPosition.getAccuracy());
+ ctx.fireChannelRead(position);
+ return;
+ }
+ }
+
if (statisticsManager != null) {
statisticsManager.registerGeolocationRequest();
}
@@ -56,15 +73,7 @@ public class GeolocationHandler extends ChannelInboundHandlerAdapter {
new GeolocationProvider.LocationProviderCallback() {
@Override
public void onSuccess(double latitude, double longitude, double accuracy) {
- position.set(Position.KEY_APPROXIMATE, true);
- position.setValid(true);
- position.setFixTime(position.getDeviceTime());
- position.setLatitude(latitude);
- position.setLongitude(longitude);
- position.setAccuracy(accuracy);
- position.setAltitude(0);
- position.setSpeed(0);
- position.setCourse(0);
+ updatePosition(position, latitude, longitude, accuracy);
ctx.fireChannelRead(position);
}
@@ -82,4 +91,16 @@ public class GeolocationHandler extends ChannelInboundHandlerAdapter {
}
}
+ private void updatePosition(Position position, double latitude, double longitude, double accuracy) {
+ position.set(Position.KEY_APPROXIMATE, true);
+ position.setValid(true);
+ position.setFixTime(position.getDeviceTime());
+ position.setLatitude(latitude);
+ position.setLongitude(longitude);
+ position.setAccuracy(accuracy);
+ position.setAltitude(0);
+ position.setSpeed(0);
+ position.setCourse(0);
+ }
+
}
diff --git a/src/main/java/org/traccar/handler/HemisphereHandler.java b/src/main/java/org/traccar/handler/HemisphereHandler.java
index aff3d8a64..ccbde9fe5 100644
--- a/src/main/java/org/traccar/handler/HemisphereHandler.java
+++ b/src/main/java/org/traccar/handler/HemisphereHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,12 +21,17 @@ import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.model.Position;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton
@ChannelHandler.Sharable
public class HemisphereHandler extends BaseDataHandler {
private int latitudeFactor;
private int longitudeFactor;
+ @Inject
public HemisphereHandler(Config config) {
String latitudeHemisphere = config.getString(Keys.LOCATION_LATITUDE_HEMISPHERE);
if (latitudeHemisphere != null) {
@@ -36,7 +41,7 @@ public class HemisphereHandler extends BaseDataHandler {
latitudeFactor = -1;
}
}
- String longitudeHemisphere = config.getString(Keys.LOCATION_LATITUDE_HEMISPHERE);
+ String longitudeHemisphere = config.getString(Keys.LOCATION_LONGITUDE_HEMISPHERE);
if (longitudeHemisphere != null) {
if (longitudeHemisphere.equalsIgnoreCase("E")) {
longitudeFactor = 1;
diff --git a/src/main/java/org/traccar/handler/MotionHandler.java b/src/main/java/org/traccar/handler/MotionHandler.java
index e8051dd75..10312f9b3 100644
--- a/src/main/java/org/traccar/handler/MotionHandler.java
+++ b/src/main/java/org/traccar/handler/MotionHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,19 +19,25 @@ package org.traccar.handler;
import io.netty.channel.ChannelHandler;
import org.traccar.BaseDataHandler;
import org.traccar.model.Position;
+import org.traccar.reports.common.TripsConfig;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton
@ChannelHandler.Sharable
public class MotionHandler extends BaseDataHandler {
- private double speedThreshold;
+ private final double speedThreshold;
- public MotionHandler(double speedThreshold) {
- this.speedThreshold = speedThreshold;
+ @Inject
+ public MotionHandler(TripsConfig tripsConfig) {
+ speedThreshold = tripsConfig.getSpeedThreshold();
}
@Override
protected Position handlePosition(Position position) {
- if (!position.getAttributes().containsKey(Position.KEY_MOTION)) {
+ if (!position.hasAttribute(Position.KEY_MOTION)) {
position.set(Position.KEY_MOTION, position.getSpeed() > speedThreshold);
}
return position;
diff --git a/src/main/java/org/traccar/handler/OpenChannelHandler.java b/src/main/java/org/traccar/handler/OpenChannelHandler.java
index d09d617ab..e416f35ae 100644
--- a/src/main/java/org/traccar/handler/OpenChannelHandler.java
+++ b/src/main/java/org/traccar/handler/OpenChannelHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,26 +17,26 @@ package org.traccar.handler;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
-import org.traccar.TrackerServer;
+import org.traccar.TrackerConnector;
public class OpenChannelHandler extends ChannelDuplexHandler {
- private final TrackerServer server;
+ private final TrackerConnector connector;
- public OpenChannelHandler(TrackerServer server) {
- this.server = server;
+ public OpenChannelHandler(TrackerConnector connector) {
+ this.connector = connector;
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
- server.getChannelGroup().add(ctx.channel());
+ connector.getChannelGroup().add(ctx.channel());
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
super.channelInactive(ctx);
- server.getChannelGroup().remove(ctx.channel());
+ connector.getChannelGroup().remove(ctx.channel());
}
}
diff --git a/src/main/java/org/traccar/handler/RemoteAddressHandler.java b/src/main/java/org/traccar/handler/RemoteAddressHandler.java
index c09b8c39a..e18d34ef2 100644
--- a/src/main/java/org/traccar/handler/RemoteAddressHandler.java
+++ b/src/main/java/org/traccar/handler/RemoteAddressHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,22 +18,36 @@ package org.traccar.handler;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
import org.traccar.model.Position;
+import javax.inject.Inject;
+import javax.inject.Singleton;
import java.net.InetSocketAddress;
+@Singleton
@ChannelHandler.Sharable
public class RemoteAddressHandler extends ChannelInboundHandlerAdapter {
+ private final boolean enabled;
+
+ @Inject
+ public RemoteAddressHandler(Config config) {
+ enabled = config.getBoolean(Keys.PROCESSING_REMOTE_ADDRESS_ENABLE);
+ }
+
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
- InetSocketAddress remoteAddress = (InetSocketAddress) ctx.channel().remoteAddress();
- String hostAddress = remoteAddress != null ? remoteAddress.getAddress().getHostAddress() : null;
+ if (enabled) {
+ InetSocketAddress remoteAddress = (InetSocketAddress) ctx.channel().remoteAddress();
+ String hostAddress = remoteAddress != null ? remoteAddress.getAddress().getHostAddress() : null;
- if (msg instanceof Position) {
- Position position = (Position) msg;
- position.set(Position.KEY_IP, hostAddress);
+ if (msg instanceof Position) {
+ Position position = (Position) msg;
+ position.set(Position.KEY_IP, hostAddress);
+ }
}
ctx.fireChannelRead(msg);
diff --git a/src/main/java/org/traccar/handler/SpeedLimitHandler.java b/src/main/java/org/traccar/handler/SpeedLimitHandler.java
index 65f2c9cfe..0c6025999 100644
--- a/src/main/java/org/traccar/handler/SpeedLimitHandler.java
+++ b/src/main/java/org/traccar/handler/SpeedLimitHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2020 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,10 @@ import org.slf4j.LoggerFactory;
import org.traccar.model.Position;
import org.traccar.speedlimit.SpeedLimitProvider;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton
@ChannelHandler.Sharable
public class SpeedLimitHandler extends ChannelInboundHandlerAdapter {
@@ -30,6 +34,7 @@ public class SpeedLimitHandler extends ChannelInboundHandlerAdapter {
private final SpeedLimitProvider speedLimitProvider;
+ @Inject
public SpeedLimitHandler(SpeedLimitProvider speedLimitProvider) {
this.speedLimitProvider = speedLimitProvider;
}
diff --git a/src/main/java/org/traccar/handler/StandardLoggingHandler.java b/src/main/java/org/traccar/handler/StandardLoggingHandler.java
index 13c5f8281..84492e2a5 100644
--- a/src/main/java/org/traccar/handler/StandardLoggingHandler.java
+++ b/src/main/java/org/traccar/handler/StandardLoggingHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import io.netty.channel.ChannelPromise;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.NetworkMessage;
+import org.traccar.helper.NetworkUtil;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
@@ -63,7 +64,7 @@ public class StandardLoggingHandler extends ChannelDuplexHandler {
public void log(ChannelHandlerContext ctx, boolean downstream, SocketAddress remoteAddress, ByteBuf buf) {
StringBuilder message = new StringBuilder();
- message.append("[").append(ctx.channel().id().asShortText()).append(": ");
+ message.append("[").append(NetworkUtil.session(ctx.channel())).append(": ");
message.append(protocol);
if (downstream) {
message.append(" > ");
@@ -76,9 +77,8 @@ public class StandardLoggingHandler extends ChannelDuplexHandler {
} else {
message.append("unknown");
}
- message.append("]");
+ message.append("] ");
- message.append(" HEX: ");
message.append(ByteBufUtil.hexDump(buf));
LOGGER.info(message.toString());
diff --git a/src/main/java/org/traccar/handler/TimeHandler.java b/src/main/java/org/traccar/handler/TimeHandler.java
index 822c22a0a..c98b0bd4c 100644
--- a/src/main/java/org/traccar/handler/TimeHandler.java
+++ b/src/main/java/org/traccar/handler/TimeHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,24 +19,33 @@ import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.model.Position;
+import javax.inject.Inject;
+import javax.inject.Singleton;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
+@Singleton
@ChannelHandler.Sharable
public class TimeHandler extends ChannelInboundHandlerAdapter {
+ private final boolean enabled;
private final boolean useServerTime;
private final Set<String> protocols;
+ @Inject
public TimeHandler(Config config) {
- useServerTime = config.getString(Keys.TIME_OVERRIDE).equalsIgnoreCase("serverTime");
- String protocolList = Context.getConfig().getString(Keys.TIME_PROTOCOLS);
+ enabled = config.hasKey(Keys.TIME_OVERRIDE);
+ if (enabled) {
+ useServerTime = config.getString(Keys.TIME_OVERRIDE).equalsIgnoreCase("serverTime");
+ } else {
+ useServerTime = false;
+ }
+ String protocolList = config.getString(Keys.TIME_PROTOCOLS);
if (protocolList != null) {
protocols = new HashSet<>(Arrays.asList(protocolList.split("[, ]")));
} else {
@@ -47,7 +56,7 @@ public class TimeHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
- if (msg instanceof Position && (protocols == null
+ if (enabled && msg instanceof Position && (protocols == null
|| protocols.contains(ctx.pipeline().get(BaseProtocolDecoder.class).getProtocolName()))) {
Position position = (Position) msg;
diff --git a/src/main/java/org/traccar/handler/events/AlertEventHandler.java b/src/main/java/org/traccar/handler/events/AlertEventHandler.java
index 05dbc516e..9f77df989 100644
--- a/src/main/java/org/traccar/handler/events/AlertEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/AlertEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,18 +21,23 @@ import java.util.Map;
import io.netty.channel.ChannelHandler;
import org.traccar.config.Config;
import org.traccar.config.Keys;
-import org.traccar.database.IdentityManager;
import org.traccar.model.Event;
import org.traccar.model.Position;
+import org.traccar.session.cache.CacheManager;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton
@ChannelHandler.Sharable
public class AlertEventHandler extends BaseEventHandler {
- private final IdentityManager identityManager;
+ private final CacheManager cacheManager;
private final boolean ignoreDuplicateAlerts;
- public AlertEventHandler(Config config, IdentityManager identityManager) {
- this.identityManager = identityManager;
+ @Inject
+ public AlertEventHandler(Config config, CacheManager cacheManager) {
+ this.cacheManager = cacheManager;
ignoreDuplicateAlerts = config.getBoolean(Keys.EVENT_IGNORE_DUPLICATE_ALERTS);
}
@@ -42,7 +47,7 @@ public class AlertEventHandler extends BaseEventHandler {
if (alarm != null) {
boolean ignoreAlert = false;
if (ignoreDuplicateAlerts) {
- Position lastPosition = identityManager.getLastPosition(position.getDeviceId());
+ Position lastPosition = cacheManager.getPosition(position.getDeviceId());
if (lastPosition != null && alarm.equals(lastPosition.getAttributes().get(Position.KEY_ALARM))) {
ignoreAlert = true;
}
diff --git a/src/main/java/org/traccar/handler/events/BaseEventHandler.java b/src/main/java/org/traccar/handler/events/BaseEventHandler.java
index 41f677f6c..271aaa35d 100644
--- a/src/main/java/org/traccar/handler/events/BaseEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/BaseEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,17 +18,26 @@ package org.traccar.handler.events;
import java.util.Map;
import org.traccar.BaseDataHandler;
-import org.traccar.Context;
+import org.traccar.database.NotificationManager;
import org.traccar.model.Event;
import org.traccar.model.Position;
+import javax.inject.Inject;
+
public abstract class BaseEventHandler extends BaseDataHandler {
+ private NotificationManager notificationManager;
+
+ @Inject
+ public void setNotificationManager(NotificationManager notificationManager) {
+ this.notificationManager = notificationManager;
+ }
+
@Override
protected Position handlePosition(Position position) {
Map<Event, Position> events = analyzePosition(position);
- if (events != null && Context.getNotificationManager() != null) {
- Context.getNotificationManager().updateEvents(events);
+ if (events != null && !events.isEmpty()) {
+ notificationManager.updateEvents(events);
}
return position;
}
diff --git a/src/main/java/org/traccar/handler/events/BehaviorEventHandler.java b/src/main/java/org/traccar/handler/events/BehaviorEventHandler.java
index 767cef3f6..51bbd82d6 100644
--- a/src/main/java/org/traccar/handler/events/BehaviorEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/BehaviorEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,32 +18,36 @@ package org.traccar.handler.events;
import io.netty.channel.ChannelHandler;
import org.traccar.config.Config;
import org.traccar.config.Keys;
-import org.traccar.database.IdentityManager;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Event;
import org.traccar.model.Position;
+import org.traccar.session.cache.CacheManager;
+import javax.inject.Inject;
+import javax.inject.Singleton;
import java.util.Collections;
import java.util.Map;
+@Singleton
@ChannelHandler.Sharable
public class BehaviorEventHandler extends BaseEventHandler {
private final double accelerationThreshold;
private final double brakingThreshold;
- private final IdentityManager identityManager;
+ private final CacheManager cacheManager;
- public BehaviorEventHandler(Config config, IdentityManager identityManager) {
+ @Inject
+ public BehaviorEventHandler(Config config, CacheManager cacheManager) {
accelerationThreshold = config.getDouble(Keys.EVENT_BEHAVIOR_ACCELERATION_THRESHOLD);
brakingThreshold = config.getDouble(Keys.EVENT_BEHAVIOR_BRAKING_THRESHOLD);
- this.identityManager = identityManager;
+ this.cacheManager = cacheManager;
}
@Override
protected Map<Event, Position> analyzePosition(Position position) {
- Position lastPosition = identityManager.getLastPosition(position.getDeviceId());
+ Position lastPosition = cacheManager.getPosition(position.getDeviceId());
if (lastPosition != null && position.getFixTime().equals(lastPosition.getFixTime())) {
double acceleration = UnitsConverter.mpsFromKnots(position.getSpeed() - lastPosition.getSpeed()) * 1000
/ (position.getFixTime().getTime() - lastPosition.getFixTime().getTime());
diff --git a/src/main/java/org/traccar/handler/events/CommandResultEventHandler.java b/src/main/java/org/traccar/handler/events/CommandResultEventHandler.java
index 9b7ff554e..772176e9c 100644
--- a/src/main/java/org/traccar/handler/events/CommandResultEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/CommandResultEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,9 +22,17 @@ import io.netty.channel.ChannelHandler;
import org.traccar.model.Event;
import org.traccar.model.Position;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton
@ChannelHandler.Sharable
public class CommandResultEventHandler extends BaseEventHandler {
+ @Inject
+ public CommandResultEventHandler() {
+ }
+
@Override
protected Map<Event, Position> analyzePosition(Position position) {
Object commandResult = position.getAttributes().get(Position.KEY_RESULT);
diff --git a/src/main/java/org/traccar/handler/events/DriverEventHandler.java b/src/main/java/org/traccar/handler/events/DriverEventHandler.java
index 6fdf4246b..51fdc0307 100644
--- a/src/main/java/org/traccar/handler/events/DriverEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/DriverEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,32 +16,37 @@
*/
package org.traccar.handler.events;
-import java.util.Collections;
-import java.util.Map;
-
import io.netty.channel.ChannelHandler;
-import org.traccar.database.IdentityManager;
+import org.traccar.helper.model.PositionUtil;
import org.traccar.model.Event;
import org.traccar.model.Position;
+import org.traccar.session.cache.CacheManager;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.util.Collections;
+import java.util.Map;
+@Singleton
@ChannelHandler.Sharable
public class DriverEventHandler extends BaseEventHandler {
- private final IdentityManager identityManager;
+ private final CacheManager cacheManager;
- public DriverEventHandler(IdentityManager identityManager) {
- this.identityManager = identityManager;
+ @Inject
+ public DriverEventHandler(CacheManager cacheManager) {
+ this.cacheManager = cacheManager;
}
@Override
protected Map<Event, Position> analyzePosition(Position position) {
- if (!identityManager.isLatestPosition(position)) {
+ if (!PositionUtil.isLatest(cacheManager, position)) {
return null;
}
String driverUniqueId = position.getString(Position.KEY_DRIVER_UNIQUE_ID);
if (driverUniqueId != null) {
String oldDriverUniqueId = null;
- Position lastPosition = identityManager.getLastPosition(position.getDeviceId());
+ Position lastPosition = cacheManager.getPosition(position.getDeviceId());
if (lastPosition != null) {
oldDriverUniqueId = lastPosition.getString(Position.KEY_DRIVER_UNIQUE_ID);
}
diff --git a/src/main/java/org/traccar/handler/events/FuelDropEventHandler.java b/src/main/java/org/traccar/handler/events/FuelDropEventHandler.java
deleted file mode 100644
index 343a17311..000000000
--- a/src/main/java/org/traccar/handler/events/FuelDropEventHandler.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2017 - 2019 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.handler.events;
-
-import io.netty.channel.ChannelHandler;
-import org.traccar.database.IdentityManager;
-import org.traccar.model.Device;
-import org.traccar.model.Event;
-import org.traccar.model.Position;
-
-import java.util.Collections;
-import java.util.Map;
-
-@ChannelHandler.Sharable
-public class FuelDropEventHandler extends BaseEventHandler {
-
- public static final String ATTRIBUTE_FUEL_DROP_THRESHOLD = "fuelDropThreshold";
-
- private final IdentityManager identityManager;
-
- public FuelDropEventHandler(IdentityManager identityManager) {
- this.identityManager = identityManager;
- }
-
- @Override
- protected Map<Event, Position> analyzePosition(Position position) {
-
- Device device = identityManager.getById(position.getDeviceId());
- if (device == null) {
- return null;
- }
- if (!identityManager.isLatestPosition(position)) {
- return null;
- }
-
- double fuelDropThreshold = identityManager
- .lookupAttributeDouble(device.getId(), ATTRIBUTE_FUEL_DROP_THRESHOLD, 0, true, false);
-
- if (fuelDropThreshold > 0) {
- Position lastPosition = identityManager.getLastPosition(position.getDeviceId());
- if (position.getAttributes().containsKey(Position.KEY_FUEL_LEVEL)
- && lastPosition != null && lastPosition.getAttributes().containsKey(Position.KEY_FUEL_LEVEL)) {
-
- double drop = lastPosition.getDouble(Position.KEY_FUEL_LEVEL)
- - position.getDouble(Position.KEY_FUEL_LEVEL);
- if (drop >= fuelDropThreshold) {
- Event event = new Event(Event.TYPE_DEVICE_FUEL_DROP, position);
- event.set(ATTRIBUTE_FUEL_DROP_THRESHOLD, fuelDropThreshold);
- return Collections.singletonMap(event, position);
- }
- }
- }
-
- return null;
- }
-
-}
diff --git a/src/main/java/org/traccar/handler/events/FuelEventHandler.java b/src/main/java/org/traccar/handler/events/FuelEventHandler.java
new file mode 100644
index 000000000..462cc4223
--- /dev/null
+++ b/src/main/java/org/traccar/handler/events/FuelEventHandler.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.handler.events;
+
+import io.netty.channel.ChannelHandler;
+import org.traccar.config.Keys;
+import org.traccar.helper.model.AttributeUtil;
+import org.traccar.helper.model.PositionUtil;
+import org.traccar.model.Device;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+import org.traccar.session.cache.CacheManager;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.util.Map;
+
+@Singleton
+@ChannelHandler.Sharable
+public class FuelEventHandler extends BaseEventHandler {
+
+ private final CacheManager cacheManager;
+
+ @Inject
+ public FuelEventHandler(CacheManager cacheManager) {
+ this.cacheManager = cacheManager;
+ }
+
+ @Override
+ protected Map<Event, Position> analyzePosition(Position position) {
+
+ Device device = cacheManager.getObject(Device.class, position.getDeviceId());
+ if (device == null) {
+ return null;
+ }
+ if (!PositionUtil.isLatest(cacheManager, position)) {
+ return null;
+ }
+
+ if (position.hasAttribute(Position.KEY_FUEL_LEVEL)) {
+ Position lastPosition = cacheManager.getPosition(position.getDeviceId());
+ if (lastPosition != null && lastPosition.hasAttribute(Position.KEY_FUEL_LEVEL)) {
+ double before = lastPosition.getDouble(Position.KEY_FUEL_LEVEL);
+ double after = position.getDouble(Position.KEY_FUEL_LEVEL);
+ double change = after - before;
+
+ if (change > 0) {
+ double threshold = AttributeUtil.lookup(
+ cacheManager, Keys.EVENT_FUEL_INCREASE_THRESHOLD, position.getDeviceId());
+ if (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) {
+ return Map.of(new Event(Event.TYPE_DEVICE_FUEL_DROP, position), position);
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/handler/events/GeofenceEventHandler.java b/src/main/java/org/traccar/handler/events/GeofenceEventHandler.java
index dae0c891f..9414f4b31 100644
--- a/src/main/java/org/traccar/handler/events/GeofenceEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/GeofenceEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,49 +15,59 @@
*/
package org.traccar.handler.events;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
import io.netty.channel.ChannelHandler;
-import org.traccar.database.CalendarManager;
-import org.traccar.database.ConnectionManager;
-import org.traccar.database.GeofenceManager;
-import org.traccar.database.IdentityManager;
+import org.traccar.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 java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+@Singleton
@ChannelHandler.Sharable
public class GeofenceEventHandler extends BaseEventHandler {
- private final IdentityManager identityManager;
- private final GeofenceManager geofenceManager;
- private final CalendarManager calendarManager;
+ private final Config config;
+ private final CacheManager cacheManager;
private final ConnectionManager connectionManager;
+ private final Storage storage;
+ @Inject
public GeofenceEventHandler(
- IdentityManager identityManager, GeofenceManager geofenceManager, CalendarManager calendarManager,
- ConnectionManager connectionManager) {
- this.identityManager = identityManager;
- this.geofenceManager = geofenceManager;
- this.calendarManager = calendarManager;
+ Config config, CacheManager cacheManager, ConnectionManager connectionManager, Storage storage) {
+ this.config = config;
+ this.cacheManager = cacheManager;
this.connectionManager = connectionManager;
+ this.storage = storage;
}
@Override
protected Map<Event, Position> analyzePosition(Position position) {
- Device device = identityManager.getById(position.getDeviceId());
+ Device device = cacheManager.getObject(Device.class, position.getDeviceId());
if (device == null) {
return null;
}
- if (!identityManager.isLatestPosition(position) || !position.getValid()) {
+ if (!PositionUtil.isLatest(cacheManager, position) || !position.getValid()) {
return null;
}
- List<Long> currentGeofences = geofenceManager.getCurrentDeviceGeofences(position);
+ List<Long> currentGeofences = GeofenceUtil.getCurrentGeofences(config, cacheManager, position);
List<Long> oldGeofences = new ArrayList<>();
if (device.getGeofenceIds() != null) {
oldGeofences.addAll(device.getGeofenceIds());
@@ -66,15 +76,25 @@ public class GeofenceEventHandler extends BaseEventHandler {
newGeofences.removeAll(oldGeofences);
oldGeofences.removeAll(currentGeofences);
- device.setGeofenceIds(currentGeofences);
+
if (!oldGeofences.isEmpty() || !newGeofences.isEmpty()) {
- connectionManager.updateDevice(device);
+ 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);
}
Map<Event, Position> events = new HashMap<>();
for (long geofenceId : oldGeofences) {
- long calendarId = geofenceManager.getById(geofenceId).getCalendarId();
- Calendar calendar = calendarId != 0 ? calendarManager.getById(calendarId) : null;
+ long calendarId = cacheManager.getObject(Geofence.class, geofenceId).getCalendarId();
+ Calendar calendar = calendarId != 0 ? cacheManager.getObject(Calendar.class, calendarId) : null;
if (calendar == null || calendar.checkMoment(position.getFixTime())) {
Event event = new Event(Event.TYPE_GEOFENCE_EXIT, position);
event.setGeofenceId(geofenceId);
@@ -82,8 +102,8 @@ public class GeofenceEventHandler extends BaseEventHandler {
}
}
for (long geofenceId : newGeofences) {
- long calendarId = geofenceManager.getById(geofenceId).getCalendarId();
- Calendar calendar = calendarId != 0 ? calendarManager.getById(calendarId) : null;
+ long calendarId = cacheManager.getObject(Geofence.class, geofenceId).getCalendarId();
+ Calendar calendar = calendarId != 0 ? cacheManager.getObject(Calendar.class, calendarId) : null;
if (calendar == null || calendar.checkMoment(position.getFixTime())) {
Event event = new Event(Event.TYPE_GEOFENCE_ENTER, position);
event.setGeofenceId(geofenceId);
diff --git a/src/main/java/org/traccar/handler/events/IgnitionEventHandler.java b/src/main/java/org/traccar/handler/events/IgnitionEventHandler.java
index 69df9a46b..b2e9a3325 100644
--- a/src/main/java/org/traccar/handler/events/IgnitionEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/IgnitionEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,34 +20,40 @@ import java.util.Collections;
import java.util.Map;
import io.netty.channel.ChannelHandler;
-import org.traccar.database.IdentityManager;
+import org.traccar.helper.model.PositionUtil;
import org.traccar.model.Device;
import org.traccar.model.Event;
import org.traccar.model.Position;
+import org.traccar.session.cache.CacheManager;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton
@ChannelHandler.Sharable
public class IgnitionEventHandler extends BaseEventHandler {
- private final IdentityManager identityManager;
+ private final CacheManager cacheManager;
- public IgnitionEventHandler(IdentityManager identityManager) {
- this.identityManager = identityManager;
+ @Inject
+ public IgnitionEventHandler(CacheManager cacheManager) {
+ this.cacheManager = cacheManager;
}
@Override
protected Map<Event, Position> analyzePosition(Position position) {
- Device device = identityManager.getById(position.getDeviceId());
- if (device == null || !identityManager.isLatestPosition(position)) {
+ Device device = cacheManager.getObject(Device.class, position.getDeviceId());
+ if (device == null || !PositionUtil.isLatest(cacheManager, position)) {
return null;
}
Map<Event, Position> result = null;
- if (position.getAttributes().containsKey(Position.KEY_IGNITION)) {
+ if (position.hasAttribute(Position.KEY_IGNITION)) {
boolean ignition = position.getBoolean(Position.KEY_IGNITION);
- Position lastPosition = identityManager.getLastPosition(position.getDeviceId());
- if (lastPosition != null && lastPosition.getAttributes().containsKey(Position.KEY_IGNITION)) {
+ Position lastPosition = cacheManager.getPosition(position.getDeviceId());
+ if (lastPosition != null && lastPosition.hasAttribute(Position.KEY_IGNITION)) {
boolean oldIgnition = lastPosition.getBoolean(Position.KEY_IGNITION);
if (ignition && !oldIgnition) {
diff --git a/src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java b/src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java
index 0f960ad1f..909950acf 100644
--- a/src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2016 - 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,48 +20,46 @@ import java.util.HashMap;
import java.util.Map;
import io.netty.channel.ChannelHandler;
-import org.traccar.database.IdentityManager;
-import org.traccar.database.MaintenancesManager;
import org.traccar.model.Event;
import org.traccar.model.Maintenance;
import org.traccar.model.Position;
+import org.traccar.session.cache.CacheManager;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton
@ChannelHandler.Sharable
public class MaintenanceEventHandler extends BaseEventHandler {
- private final IdentityManager identityManager;
- private final MaintenancesManager maintenancesManager;
+ private final CacheManager cacheManager;
- public MaintenanceEventHandler(IdentityManager identityManager, MaintenancesManager maintenancesManager) {
- this.identityManager = identityManager;
- this.maintenancesManager = maintenancesManager;
+ @Inject
+ public MaintenanceEventHandler(CacheManager cacheManager) {
+ this.cacheManager = cacheManager;
}
@Override
protected Map<Event, Position> analyzePosition(Position position) {
- if (identityManager.getById(position.getDeviceId()) == null
- || !identityManager.isLatestPosition(position)) {
- return null;
- }
-
- Position lastPosition = identityManager.getLastPosition(position.getDeviceId());
- if (lastPosition == null) {
+ Position lastPosition = cacheManager.getPosition(position.getDeviceId());
+ if (lastPosition == null || position.getFixTime().compareTo(lastPosition.getFixTime()) < 0) {
return null;
}
Map<Event, Position> events = new HashMap<>();
- for (long maintenanceId : maintenancesManager.getAllDeviceItems(position.getDeviceId())) {
- Maintenance maintenance = maintenancesManager.getById(maintenanceId);
+ for (Maintenance maintenance : cacheManager.getDeviceObjects(position.getDeviceId(), Maintenance.class)) {
if (maintenance.getPeriod() != 0) {
double oldValue = lastPosition.getDouble(maintenance.getType());
double newValue = position.getDouble(maintenance.getType());
- if (oldValue != 0.0 && newValue != 0.0
- && (long) ((oldValue - maintenance.getStart()) / maintenance.getPeriod())
+ if (oldValue != 0.0 && newValue != 0.0 && newValue >= maintenance.getStart()) {
+ if (oldValue < maintenance.getStart()
+ || (long) ((oldValue - maintenance.getStart()) / maintenance.getPeriod())
< (long) ((newValue - maintenance.getStart()) / maintenance.getPeriod())) {
- Event event = new Event(Event.TYPE_MAINTENANCE, position);
- event.setMaintenanceId(maintenanceId);
- event.set(maintenance.getType(), newValue);
- events.put(event, position);
+ Event event = new Event(Event.TYPE_MAINTENANCE, position);
+ event.setMaintenanceId(maintenance.getId());
+ event.set(maintenance.getType(), newValue);
+ events.put(event, position);
+ }
}
}
}
diff --git a/src/main/java/org/traccar/handler/events/MediaEventHandler.java b/src/main/java/org/traccar/handler/events/MediaEventHandler.java
new file mode 100644
index 000000000..a49e08e8d
--- /dev/null
+++ b/src/main/java/org/traccar/handler/events/MediaEventHandler.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.handler.events;
+
+import io.netty.channel.ChannelHandler;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+@Singleton
+@ChannelHandler.Sharable
+public class MediaEventHandler extends BaseEventHandler {
+
+ @Inject
+ public MediaEventHandler() {
+ }
+
+ @Override
+ protected Map<Event, Position> analyzePosition(Position position) {
+ return Stream.of(Position.KEY_IMAGE, Position.KEY_VIDEO, Position.KEY_AUDIO)
+ .filter(position::hasAttribute)
+ .map(type -> {
+ Event event = new Event(Event.TYPE_MEDIA, position);
+ event.set("media", type);
+ event.set("file", position.getString(type));
+ return event;
+ })
+ .collect(Collectors.toMap(event -> event, event -> position));
+ }
+
+}
diff --git a/src/main/java/org/traccar/handler/events/MotionEventHandler.java b/src/main/java/org/traccar/handler/events/MotionEventHandler.java
index db276f32b..c406bd935 100644
--- a/src/main/java/org/traccar/handler/events/MotionEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/MotionEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,120 +16,72 @@
*/
package org.traccar.handler.events;
-import java.util.Collections;
-import java.util.Map;
-
import io.netty.channel.ChannelHandler;
-import org.traccar.database.DeviceManager;
-import org.traccar.database.IdentityManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.helper.model.PositionUtil;
import org.traccar.model.Device;
-import org.traccar.model.DeviceState;
import org.traccar.model.Event;
import org.traccar.model.Position;
-import org.traccar.reports.ReportUtils;
-import org.traccar.reports.model.TripsConfig;
+import org.traccar.reports.common.TripsConfig;
+import org.traccar.session.cache.CacheManager;
+import org.traccar.session.state.MotionProcessor;
+import org.traccar.session.state.MotionState;
+import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Request;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.util.Collections;
+import java.util.Map;
+@Singleton
@ChannelHandler.Sharable
public class MotionEventHandler extends BaseEventHandler {
- private final IdentityManager identityManager;
- private final DeviceManager deviceManager;
+ private static final Logger LOGGER = LoggerFactory.getLogger(MotionEventHandler.class);
+
+ private final CacheManager cacheManager;
+ private final Storage storage;
private final TripsConfig tripsConfig;
- public MotionEventHandler(IdentityManager identityManager, DeviceManager deviceManager, TripsConfig tripsConfig) {
- this.identityManager = identityManager;
- this.deviceManager = deviceManager;
+ @Inject
+ public MotionEventHandler(
+ CacheManager cacheManager, Storage storage, TripsConfig tripsConfig) {
+ this.cacheManager = cacheManager;
+ this.storage = storage;
this.tripsConfig = tripsConfig;
}
- private Map<Event, Position> newEvent(DeviceState deviceState, boolean newMotion) {
- String eventType = newMotion ? Event.TYPE_DEVICE_MOVING : Event.TYPE_DEVICE_STOPPED;
- Position position = deviceState.getMotionPosition();
- Event event = new Event(eventType, position);
- deviceState.setMotionState(newMotion);
- deviceState.setMotionPosition(null);
- return Collections.singletonMap(event, position);
- }
-
- public Map<Event, Position> updateMotionState(DeviceState deviceState) {
- Map<Event, Position> result = null;
- if (deviceState.getMotionState() != null && deviceState.getMotionPosition() != null) {
- boolean newMotion = !deviceState.getMotionState();
- Position motionPosition = deviceState.getMotionPosition();
- long currentTime = System.currentTimeMillis();
- long motionTime = motionPosition.getFixTime().getTime()
- + (newMotion ? tripsConfig.getMinimalTripDuration() : tripsConfig.getMinimalParkingDuration());
- if (motionTime <= currentTime) {
- result = newEvent(deviceState, newMotion);
- }
- }
- return result;
- }
-
- public Map<Event, Position> updateMotionState(DeviceState deviceState, Position position) {
- return updateMotionState(deviceState, position, position.getBoolean(Position.KEY_MOTION));
- }
-
- public Map<Event, Position> updateMotionState(DeviceState deviceState, Position position, boolean newMotion) {
- Map<Event, Position> result = null;
- Boolean oldMotion = deviceState.getMotionState();
-
- long currentTime = position.getFixTime().getTime();
- if (newMotion != oldMotion) {
- if (deviceState.getMotionPosition() == null) {
- deviceState.setMotionPosition(position);
- }
- } else {
- deviceState.setMotionPosition(null);
- }
-
- Position motionPosition = deviceState.getMotionPosition();
- if (motionPosition != null) {
- long motionTime = motionPosition.getFixTime().getTime();
- double distance = ReportUtils.calculateDistance(motionPosition, position, false);
- Boolean ignition = null;
- if (tripsConfig.getUseIgnition()
- && position.getAttributes().containsKey(Position.KEY_IGNITION)) {
- ignition = position.getBoolean(Position.KEY_IGNITION);
- }
- if (newMotion) {
- if (motionTime + tripsConfig.getMinimalTripDuration() <= currentTime
- || distance >= tripsConfig.getMinimalTripDistance()) {
- result = newEvent(deviceState, newMotion);
- }
- } else {
- if (motionTime + tripsConfig.getMinimalParkingDuration() <= currentTime
- || ignition != null && !ignition) {
- result = newEvent(deviceState, newMotion);
- }
- }
- }
- return result;
- }
-
@Override
protected Map<Event, Position> analyzePosition(Position position) {
long deviceId = position.getDeviceId();
- Device device = identityManager.getById(deviceId);
+ Device device = cacheManager.getObject(Device.class, deviceId);
if (device == null) {
return null;
}
- if (!identityManager.isLatestPosition(position)
+ if (!PositionUtil.isLatest(cacheManager, position)
|| !tripsConfig.getProcessInvalidPositions() && !position.getValid()) {
return null;
}
- Map<Event, Position> result = null;
- DeviceState deviceState = deviceManager.getDeviceState(deviceId);
-
- if (deviceState.getMotionState() == null) {
- deviceState.setMotionState(position.getBoolean(Position.KEY_MOTION));
- } else {
- result = updateMotionState(deviceState, position);
+ MotionState state = MotionState.fromDevice(device);
+ MotionProcessor.updateState(state, position, position.getBoolean(Position.KEY_MOTION), tripsConfig);
+ if (state.isChanged()) {
+ state.toDevice(device);
+ try {
+ storage.updateObject(device, new Request(
+ new Columns.Include("motionStreak", "motionState", "motionTime", "motionDistance"),
+ new Condition.Equals("id", device.getId())));
+ } catch (StorageException e) {
+ LOGGER.warn("Update device motion error", e);
+ }
}
- deviceManager.setDeviceState(deviceId, deviceState);
- return result;
+ return state.getEvent() != null ? Collections.singletonMap(state.getEvent(), position) : null;
}
}
diff --git a/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java b/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java
index 347ad9005..4d6aa8857 100644
--- a/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,111 +16,65 @@
*/
package org.traccar.handler.events;
-import java.util.Collections;
-import java.util.Map;
-
import io.netty.channel.ChannelHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.traccar.config.Config;
import org.traccar.config.Keys;
-import org.traccar.database.DeviceManager;
-import org.traccar.database.GeofenceManager;
+import org.traccar.helper.model.AttributeUtil;
+import org.traccar.helper.model.PositionUtil;
import org.traccar.model.Device;
-import org.traccar.model.DeviceState;
import org.traccar.model.Event;
import org.traccar.model.Geofence;
import org.traccar.model.Position;
+import org.traccar.session.cache.CacheManager;
+import org.traccar.session.state.OverspeedProcessor;
+import org.traccar.session.state.OverspeedState;
+import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Request;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.util.Collections;
+import java.util.Map;
+@Singleton
@ChannelHandler.Sharable
public class OverspeedEventHandler extends BaseEventHandler {
- public static final String ATTRIBUTE_SPEED = "speed";
- public static final String ATTRIBUTE_SPEED_LIMIT = "speedLimit";
+ private static final Logger LOGGER = LoggerFactory.getLogger(OverspeedEventHandler.class);
- private final DeviceManager deviceManager;
- private final GeofenceManager geofenceManager;
+ private final CacheManager cacheManager;
+ private final Storage storage;
- private final boolean notRepeat;
private final long minimalDuration;
private final boolean preferLowest;
- public OverspeedEventHandler(Config config, DeviceManager deviceManager, GeofenceManager geofenceManager) {
- this.deviceManager = deviceManager;
- this.geofenceManager = geofenceManager;
- notRepeat = config.getBoolean(Keys.EVENT_OVERSPEED_NOT_REPEAT);
+ @Inject
+ public OverspeedEventHandler(
+ Config config, CacheManager cacheManager, Storage storage) {
+ this.cacheManager = cacheManager;
+ this.storage = storage;
minimalDuration = config.getLong(Keys.EVENT_OVERSPEED_MINIMAL_DURATION) * 1000;
preferLowest = config.getBoolean(Keys.EVENT_OVERSPEED_PREFER_LOWEST);
}
- private Map<Event, Position> newEvent(DeviceState deviceState, double speedLimit) {
- Position position = deviceState.getOverspeedPosition();
- Event event = new Event(Event.TYPE_DEVICE_OVERSPEED, position);
- event.set(ATTRIBUTE_SPEED, deviceState.getOverspeedPosition().getSpeed());
- event.set(ATTRIBUTE_SPEED_LIMIT, speedLimit);
- event.setGeofenceId(deviceState.getOverspeedGeofenceId());
- deviceState.setOverspeedState(notRepeat);
- deviceState.setOverspeedPosition(null);
- deviceState.setOverspeedGeofenceId(0);
- return Collections.singletonMap(event, position);
- }
-
- public Map<Event, Position> updateOverspeedState(DeviceState deviceState, double speedLimit) {
- Map<Event, Position> result = null;
- if (deviceState.getOverspeedState() != null && !deviceState.getOverspeedState()
- && deviceState.getOverspeedPosition() != null && speedLimit != 0) {
- long currentTime = System.currentTimeMillis();
- Position overspeedPosition = deviceState.getOverspeedPosition();
- long overspeedTime = overspeedPosition.getFixTime().getTime();
- if (overspeedTime + minimalDuration <= currentTime) {
- result = newEvent(deviceState, speedLimit);
- }
- }
- return result;
- }
-
- public Map<Event, Position> updateOverspeedState(
- DeviceState deviceState, Position position, double speedLimit, long geofenceId) {
- Map<Event, Position> result = null;
-
- Boolean oldOverspeed = deviceState.getOverspeedState();
-
- long currentTime = position.getFixTime().getTime();
- boolean newOverspeed = position.getSpeed() > speedLimit;
- if (newOverspeed && !oldOverspeed) {
- if (deviceState.getOverspeedPosition() == null) {
- deviceState.setOverspeedPosition(position);
- deviceState.setOverspeedGeofenceId(geofenceId);
- }
- } else if (oldOverspeed && !newOverspeed) {
- deviceState.setOverspeedState(false);
- deviceState.setOverspeedPosition(null);
- deviceState.setOverspeedGeofenceId(0);
- } else {
- deviceState.setOverspeedPosition(null);
- deviceState.setOverspeedGeofenceId(0);
- }
- Position overspeedPosition = deviceState.getOverspeedPosition();
- if (overspeedPosition != null) {
- long overspeedTime = overspeedPosition.getFixTime().getTime();
- if (newOverspeed && overspeedTime + minimalDuration <= currentTime) {
- result = newEvent(deviceState, speedLimit);
- }
- }
- return result;
- }
-
@Override
protected Map<Event, Position> analyzePosition(Position position) {
long deviceId = position.getDeviceId();
- Device device = deviceManager.getById(deviceId);
+ Device device = cacheManager.getObject(Device.class, position.getDeviceId());
if (device == null) {
return null;
}
- if (!deviceManager.isLatestPosition(position) || !position.getValid()) {
+ if (!PositionUtil.isLatest(cacheManager, position) || !position.getValid()) {
return null;
}
- double speedLimit = deviceManager.lookupAttributeDouble(deviceId, ATTRIBUTE_SPEED_LIMIT, 0, true, false);
+ double speedLimit = AttributeUtil.lookup(cacheManager, Keys.EVENT_OVERSPEED_LIMIT, deviceId);
double positionSpeedLimit = position.getDouble(Position.KEY_SPEED_LIMIT);
if (positionSpeedLimit > 0) {
@@ -130,11 +84,11 @@ public class OverspeedEventHandler extends BaseEventHandler {
double geofenceSpeedLimit = 0;
long overspeedGeofenceId = 0;
- if (geofenceManager != null && device.getGeofenceIds() != null) {
+ if (device.getGeofenceIds() != null) {
for (long geofenceId : device.getGeofenceIds()) {
- Geofence geofence = geofenceManager.getById(geofenceId);
+ Geofence geofence = cacheManager.getObject(Geofence.class, geofenceId);
if (geofence != null) {
- double currentSpeedLimit = geofence.getDouble(ATTRIBUTE_SPEED_LIMIT);
+ double currentSpeedLimit = geofence.getDouble(Keys.EVENT_OVERSPEED_LIMIT.getKey());
if (currentSpeedLimit > 0 && geofenceSpeedLimit == 0
|| preferLowest && currentSpeedLimit < geofenceSpeedLimit
|| !preferLowest && currentSpeedLimit > geofenceSpeedLimit) {
@@ -152,18 +106,19 @@ public class OverspeedEventHandler extends BaseEventHandler {
return null;
}
- Map<Event, Position> result = null;
- DeviceState deviceState = deviceManager.getDeviceState(deviceId);
-
- if (deviceState.getOverspeedState() == null) {
- deviceState.setOverspeedState(position.getSpeed() > speedLimit);
- deviceState.setOverspeedGeofenceId(position.getSpeed() > speedLimit ? overspeedGeofenceId : 0);
- } else {
- result = updateOverspeedState(deviceState, position, speedLimit, overspeedGeofenceId);
+ OverspeedState state = OverspeedState.fromDevice(device);
+ OverspeedProcessor.updateState(state, position, speedLimit, minimalDuration, overspeedGeofenceId);
+ if (state.isChanged()) {
+ state.toDevice(device);
+ try {
+ storage.updateObject(device, new Request(
+ new Columns.Include("overspeedState", "overspeedTime", "overspeedGeofenceId"),
+ new Condition.Equals("id", device.getId())));
+ } catch (StorageException e) {
+ LOGGER.warn("Update device overspeed error", e);
+ }
}
-
- deviceManager.setDeviceState(deviceId, deviceState);
- return result;
+ return state.getEvent() != null ? Collections.singletonMap(state.getEvent(), position) : null;
}
}
diff --git a/src/main/java/org/traccar/helper/BitUtil.java b/src/main/java/org/traccar/helper/BitUtil.java
index b6108edff..829ddebc9 100644
--- a/src/main/java/org/traccar/helper/BitUtil.java
+++ b/src/main/java/org/traccar/helper/BitUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,7 +21,7 @@ public final class BitUtil {
}
public static boolean check(long number, int index) {
- return (number & (1 << index)) != 0;
+ return (number & (1L << index)) != 0;
}
public static int between(int number, int from, int to) {
diff --git a/src/main/java/org/traccar/helper/BufferUtil.java b/src/main/java/org/traccar/helper/BufferUtil.java
index bbf12d738..d1025f548 100644
--- a/src/main/java/org/traccar/helper/BufferUtil.java
+++ b/src/main/java/org/traccar/helper/BufferUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,6 +27,24 @@ public final class BufferUtil {
private BufferUtil() {
}
+ public static int readSignedMagnitudeInt(ByteBuf buffer) {
+ long value = buffer.readUnsignedInt();
+ int result = (int) BitUtil.to(value, 31);
+ return BitUtil.check(value, 31) ? -result : result;
+ }
+
+ public static int indexOf(ByteBuf buffer, int fromIndex, int toIndex, byte value, int count) {
+ int startIndex = fromIndex;
+ for (int i = 0; i < count; i++) {
+ int result = buffer.indexOf(startIndex, toIndex, value);
+ if (result < 0 || i == count - 1) {
+ return result;
+ }
+ startIndex = result + 1;
+ }
+ return -1;
+ }
+
public static int indexOf(String needle, ByteBuf haystack) {
return indexOf(needle, haystack, haystack.readerIndex(), haystack.writerIndex());
}
@@ -41,16 +59,16 @@ public final class BufferUtil {
}
public static int indexOf(ByteBuf needle, ByteBuf haystack, int startIndex, int endIndex) {
- ByteBuf wrappedHaystack;
- if (startIndex == haystack.readerIndex() && endIndex == haystack.writerIndex()) {
- wrappedHaystack = haystack;
- } else {
- wrappedHaystack = Unpooled.wrappedBuffer(haystack);
- wrappedHaystack.readerIndex(startIndex - haystack.readerIndex());
- wrappedHaystack.writerIndex(endIndex - haystack.readerIndex());
+ int originalReaderIndex = haystack.readerIndex();
+ int originalWriterIndex = haystack.writerIndex();
+ try {
+ haystack.readerIndex(startIndex);
+ haystack.writerIndex(endIndex);
+ return ByteBufUtil.indexOf(needle, haystack);
+ } finally {
+ haystack.readerIndex(originalReaderIndex);
+ haystack.writerIndex(originalWriterIndex);
}
- int result = ByteBufUtil.indexOf(needle, wrappedHaystack);
- return result < 0 ? result : haystack.readerIndex() + startIndex + result;
}
}
diff --git a/src/main/java/org/traccar/helper/Checksum.java b/src/main/java/org/traccar/helper/Checksum.java
index 8c3d0063a..db5817275 100644
--- a/src/main/java/org/traccar/helper/Checksum.java
+++ b/src/main/java/org/traccar/helper/Checksum.java
@@ -200,4 +200,19 @@ public final class Checksum {
return (10 - (checksum % 10)) % 10;
}
+ public static int ip(ByteBuffer data) {
+ int sum = 0;
+ while (data.remaining() > 0) {
+ sum += data.get() & 0xff;
+ if ((sum & 0x80000000) > 0) {
+ sum = (sum & 0xffff) + (sum >> 16);
+ }
+ }
+ while ((sum >> 16) > 0) {
+ sum = (sum & 0xffff) + sum >> 16;
+ }
+ sum = (sum == 0xffff) ? sum & 0xffff : (~sum) & 0xffff;
+ return sum;
+ }
+
}
diff --git a/src/main/java/org/traccar/helper/ClassScanner.java b/src/main/java/org/traccar/helper/ClassScanner.java
new file mode 100644
index 000000000..c928f6a12
--- /dev/null
+++ b/src/main/java/org/traccar/helper/ClassScanner.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.helper;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+public final class ClassScanner {
+
+ private ClassScanner() {
+ }
+
+ public static List<Class<?>> findSubclasses(
+ Class<?> baseClass) throws IOException, URISyntaxException, ReflectiveOperationException {
+ return findSubclasses(baseClass, baseClass.getPackageName());
+ }
+
+ public static List<Class<?>> findSubclasses(Class<?> baseClass, String packageName)
+ throws IOException, URISyntaxException, ReflectiveOperationException {
+
+ List<String> names = new LinkedList<>();
+ String packagePath = packageName.replace('.', '/');
+ URL packageUrl = baseClass.getClassLoader().getResource(packagePath);
+
+ if (packageUrl.getProtocol().equals("jar")) {
+ String jarFileName = URLDecoder.decode(packageUrl.getFile(), StandardCharsets.UTF_8.name());
+ try (JarFile jf = new JarFile(jarFileName.substring(5, jarFileName.indexOf("!")))) {
+ Enumeration<JarEntry> jarEntries = jf.entries();
+ while (jarEntries.hasMoreElements()) {
+ String entryName = jarEntries.nextElement().getName();
+ if (entryName.startsWith(packagePath) && entryName.length() > packagePath.length() + 5) {
+ names.add(entryName.substring(packagePath.length() + 1, entryName.lastIndexOf('.')));
+ }
+ }
+ }
+ } else {
+ File folder = new File(new URI(packageUrl.toString()));
+ File[] files = folder.listFiles();
+ if (files != null) {
+ for (File actual: files) {
+ String entryName = actual.getName();
+ names.add(entryName.substring(0, entryName.lastIndexOf('.')));
+ }
+ }
+ }
+
+ var classes = new LinkedList<Class<?>>();
+ for (String name : names) {
+ var clazz = Class.forName(packageName + '.' + name);
+ if (baseClass.isAssignableFrom(clazz)) {
+ classes.add(clazz);
+ }
+ }
+ return classes;
+ }
+
+}
diff --git a/src/main/java/org/traccar/helper/Log.java b/src/main/java/org/traccar/helper/Log.java
index 8c67f9ddc..e1b201f9f 100644
--- a/src/main/java/org/traccar/helper/Log.java
+++ b/src/main/java/org/traccar/helper/Log.java
@@ -28,6 +28,10 @@ import java.io.StringWriter;
import java.io.Writer;
import java.net.URL;
import java.nio.charset.StandardCharsets;
+import java.nio.file.FileStore;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.ConsoleHandler;
@@ -269,4 +273,18 @@ public final class Log {
return s.toString();
}
+ public static long[] getStorageSpace() {
+ long usable = 0;
+ long total = 0;
+ for (Path root : FileSystems.getDefault().getRootDirectories()) {
+ try {
+ FileStore store = Files.getFileStore(root);
+ usable += store.getUsableSpace();
+ total += store.getTotalSpace();
+ } catch (IOException ignored) {
+ }
+ }
+ return new long[]{usable, total};
+ }
+
}
diff --git a/src/main/java/org/traccar/helper/LogAction.java b/src/main/java/org/traccar/helper/LogAction.java
index d16b25483..b255b9206 100644
--- a/src/main/java/org/traccar/helper/LogAction.java
+++ b/src/main/java/org/traccar/helper/LogAction.java
@@ -47,7 +47,7 @@ public final class LogAction {
private static final String PATTERN_OBJECT = "user: %d, action: %s, object: %s, id: %d";
private static final String PATTERN_LINK = "user: %d, action: %s, owner: %s, id: %d, property: %s, id: %d";
- private static final String PATTERN_LOGIN = "user: %d, action: %s";
+ private static final String PATTERN_LOGIN = "user: %d, action: %s, from: %s";
private static final String PATTERN_LOGIN_FAILED = "login failed from: %s";
private static final String PATTERN_DEVICE_ACCUMULATORS = "user: %d, action: %s, deviceId: %d";
private static final String PATTERN_REPORT = "user: %d, report: %s, from: %s, to: %s, devices: %s, groups: %s";
@@ -72,12 +72,12 @@ public final class LogAction {
logLinkAction(ACTION_UNLINK, userId, owner, ownerId, property, propertyId);
}
- public static void login(long userId) {
- logLoginAction(ACTION_LOGIN, userId);
+ public static void login(long userId, String remoteAddress) {
+ logLoginAction(ACTION_LOGIN, userId, remoteAddress);
}
- public static void logout(long userId) {
- logLoginAction(ACTION_LOGOUT, userId);
+ public static void logout(long userId, String remoteAddress) {
+ logLoginAction(ACTION_LOGOUT, userId, remoteAddress);
}
public static void failedLogin(String remoteAddress) {
@@ -105,8 +105,11 @@ public final class LogAction {
Introspector.decapitalize(property.getSimpleName()), propertyId));
}
- private static void logLoginAction(String action, long userId) {
- LOGGER.info(String.format(PATTERN_LOGIN, userId, action));
+ private static void logLoginAction(String action, long userId, String remoteAddress) {
+ if (remoteAddress == null || remoteAddress.isEmpty()) {
+ remoteAddress = "unknown";
+ }
+ LOGGER.info(String.format(PATTERN_LOGIN, userId, action, remoteAddress));
}
public static void logReport(
diff --git a/src/main/java/org/traccar/helper/NetworkUtil.java b/src/main/java/org/traccar/helper/NetworkUtil.java
new file mode 100644
index 000000000..2fe3487da
--- /dev/null
+++ b/src/main/java/org/traccar/helper/NetworkUtil.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.helper;
+
+import io.netty.channel.Channel;
+import io.netty.channel.socket.DatagramChannel;
+
+public final class NetworkUtil {
+
+ private NetworkUtil() {
+ }
+
+ public static String session(Channel channel) {
+ char transport = channel instanceof DatagramChannel ? 'U' : 'T';
+ return transport + channel.id().asShortText();
+ }
+
+}
diff --git a/src/main/java/org/traccar/api/ObjectMapperProvider.java b/src/main/java/org/traccar/helper/ObjectMapperContextResolver.java
index f81b20917..b40e30d76 100644
--- a/src/main/java/org/traccar/api/ObjectMapperProvider.java
+++ b/src/main/java/org/traccar/helper/ObjectMapperContextResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,20 +13,26 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.api;
+package org.traccar.helper;
import com.fasterxml.jackson.databind.ObjectMapper;
-import org.traccar.Context;
+import javax.inject.Inject;
import javax.ws.rs.ext.ContextResolver;
-import javax.ws.rs.ext.Provider;
-@Provider
-public class ObjectMapperProvider implements ContextResolver<ObjectMapper> {
+// This does not work as a lambda
+public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
+
+ private final ObjectMapper objectMapper;
+
+ @Inject
+ public ObjectMapperContextResolver(ObjectMapper objectMapper) {
+ this.objectMapper = objectMapper;
+ }
@Override
- public ObjectMapper getContext(Class<?> type) {
- return Context.getObjectMapper();
+ public ObjectMapper getContext(Class<?> clazz) {
+ return objectMapper;
}
}
diff --git a/src/main/java/org/traccar/helper/Parser.java b/src/main/java/org/traccar/helper/Parser.java
index 75106e2ba..aa39e1ad7 100644
--- a/src/main/java/org/traccar/helper/Parser.java
+++ b/src/main/java/org/traccar/helper/Parser.java
@@ -48,13 +48,14 @@ public class Parser {
}
public boolean hasNext(int number) {
- String value = matcher.group(position);
- if (value != null && !value.isEmpty()) {
- return true;
- } else {
- position += number;
- return false;
+ for (int i = position; i < position + number; i++) {
+ String value = matcher.group(i);
+ if (value != null && !value.isEmpty()) {
+ return true;
+ }
}
+ position += number;
+ return false;
}
public String next() {
@@ -155,6 +156,7 @@ public class Parser {
public enum CoordinateFormat {
DEG_DEG,
+ DEG_DEG_HEM,
DEG_HEM,
DEG_MIN_MIN,
DEG_MIN_HEM,
@@ -173,6 +175,10 @@ public class Parser {
case DEG_DEG:
coordinate = Double.parseDouble(next() + '.' + next());
break;
+ case DEG_DEG_HEM:
+ coordinate = Double.parseDouble(next() + '.' + next());
+ hemisphere = next();
+ break;
case DEG_HEM:
coordinate = nextDouble(0);
hemisphere = next();
diff --git a/src/main/java/org/traccar/helper/PatternUtil.java b/src/main/java/org/traccar/helper/PatternUtil.java
index 74813e1d9..a46c7b7b4 100644
--- a/src/main/java/org/traccar/helper/PatternUtil.java
+++ b/src/main/java/org/traccar/helper/PatternUtil.java
@@ -63,7 +63,7 @@ public final class PatternUtil {
for (int i = 0; i < pattern.length(); i++) {
try {
- Matcher matcher = Pattern.compile("(" + pattern.substring(0, i) + ").*").matcher(input);
+ Matcher matcher = Pattern.compile("(" + pattern.substring(0, i) + ")[\\s\\S]*").matcher(input);
if (matcher.matches()) {
result.patternMatch = pattern.substring(0, i);
result.patternTail = pattern.substring(i);
diff --git a/src/main/java/org/traccar/helper/model/AttributeUtil.java b/src/main/java/org/traccar/helper/model/AttributeUtil.java
new file mode 100644
index 000000000..43558e8f7
--- /dev/null
+++ b/src/main/java/org/traccar/helper/model/AttributeUtil.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.helper.model;
+
+import org.traccar.config.ConfigKey;
+import org.traccar.config.KeyType;
+import org.traccar.config.Keys;
+import org.traccar.model.Device;
+import org.traccar.model.Group;
+import org.traccar.session.cache.CacheManager;
+
+public final class AttributeUtil {
+
+ private AttributeUtil() {
+ }
+
+ @SuppressWarnings({ "deprecation", "unchecked" })
+ public static <T> T lookup(CacheManager cacheManager, ConfigKey<T> key, long deviceId) {
+ Device device = cacheManager.getObject(Device.class, deviceId);
+ Object result = device.getAttributes().get(key.getKey());
+ long groupId = device.getGroupId();
+ while (result == null && groupId > 0) {
+ Group group = cacheManager.getObject(Group.class, groupId);
+ if (group != null) {
+ result = group.getAttributes().get(key.getKey());
+ groupId = group.getGroupId();
+ } else {
+ groupId = 0;
+ }
+ }
+ if (result == null && key.hasType(KeyType.SERVER)) {
+ result = cacheManager.getServer().getAttributes().get(key.getKey());
+ }
+ if (result == null && key.hasType(KeyType.CONFIG)) {
+ result = cacheManager.getConfig().getString(key.getKey());
+ }
+
+ if (result != null) {
+ Class<T> valueClass = key.getValueClass();
+ if (valueClass.equals(Boolean.class)) {
+ return (T) (result instanceof String
+ ? Boolean.parseBoolean((String) result)
+ : result);
+ } else if (valueClass.equals(Integer.class)) {
+ return (T) (Object) (result instanceof String
+ ? Integer.parseInt((String) result)
+ : ((Number) result).intValue());
+ } else if (valueClass.equals(Long.class)) {
+ return (T) (Object) (result instanceof String
+ ? Long.parseLong((String) result)
+ : ((Number) result).longValue());
+ } else if (valueClass.equals(Double.class)) {
+ return (T) (Object) (result instanceof String
+ ? Double.parseDouble((String) result)
+ : ((Number) result).doubleValue());
+ } else {
+ return (T) result;
+ }
+ }
+ return key.getDefaultValue();
+ }
+
+ public static String getDevicePassword(
+ CacheManager cacheManager, long deviceId, String protocol, String defaultPassword) {
+
+ String password = lookup(cacheManager, Keys.DEVICE_PASSWORD, deviceId);
+ if (password != null) {
+ return password;
+ }
+
+ if (protocol != null) {
+ password = cacheManager.getConfig().getString(Keys.PROTOCOL_DEVICE_PASSWORD.withPrefix(protocol));
+ if (password != null) {
+ return password;
+ }
+ }
+
+ return defaultPassword;
+ }
+
+}
diff --git a/src/main/java/org/traccar/helper/model/DeviceUtil.java b/src/main/java/org/traccar/helper/model/DeviceUtil.java
new file mode 100644
index 000000000..597078caf
--- /dev/null
+++ b/src/main/java/org/traccar/helper/model/DeviceUtil.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.helper.model;
+
+import org.traccar.model.Device;
+import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Request;
+
+public final class DeviceUtil {
+
+ private DeviceUtil() {
+ }
+
+ public static void resetStatus(Storage storage) throws StorageException {
+ storage.updateObject(new Device(), new Request(new Columns.Include("status")));
+ }
+
+}
diff --git a/src/main/java/org/traccar/helper/model/GeofenceUtil.java b/src/main/java/org/traccar/helper/model/GeofenceUtil.java
new file mode 100644
index 000000000..9f063a8b4
--- /dev/null
+++ b/src/main/java/org/traccar/helper/model/GeofenceUtil.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.helper.model;
+
+import org.traccar.config.Config;
+import org.traccar.model.Geofence;
+import org.traccar.model.Position;
+import org.traccar.session.cache.CacheManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public final class GeofenceUtil {
+
+ private GeofenceUtil() {
+ }
+
+ public static List<Long> getCurrentGeofences(Config config, CacheManager cacheManager, Position position) {
+ List<Long> result = new ArrayList<>();
+ for (Geofence geofence : cacheManager.getDeviceObjects(position.getDeviceId(), Geofence.class)) {
+ if (geofence.getGeometry().containsPoint(
+ config, geofence, position.getLatitude(), position.getLongitude())) {
+ result.add(geofence.getId());
+ }
+ }
+ return result;
+ }
+
+}
diff --git a/src/main/java/org/traccar/helper/model/PositionUtil.java b/src/main/java/org/traccar/helper/model/PositionUtil.java
new file mode 100644
index 000000000..6c380b81a
--- /dev/null
+++ b/src/main/java/org/traccar/helper/model/PositionUtil.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.helper.model;
+
+import org.traccar.model.BaseModel;
+import org.traccar.model.Device;
+import org.traccar.model.Position;
+import org.traccar.model.User;
+import org.traccar.session.cache.CacheManager;
+import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Order;
+import org.traccar.storage.query.Request;
+
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public final class PositionUtil {
+
+ private PositionUtil() {
+ }
+
+ public static boolean isLatest(CacheManager cacheManager, Position position) {
+ Position lastPosition = cacheManager.getPosition(position.getDeviceId());
+ return lastPosition == null || position.getFixTime().compareTo(lastPosition.getFixTime()) >= 0;
+ }
+
+ public static double calculateDistance(Position first, Position last, boolean useOdometer) {
+ double distance;
+ double firstOdometer = first.getDouble(Position.KEY_ODOMETER);
+ double lastOdometer = last.getDouble(Position.KEY_ODOMETER);
+
+ if (useOdometer && firstOdometer != 0.0 && lastOdometer != 0.0) {
+ distance = lastOdometer - firstOdometer;
+ } else {
+ distance = last.getDouble(Position.KEY_TOTAL_DISTANCE) - first.getDouble(Position.KEY_TOTAL_DISTANCE);
+ }
+ return distance;
+ }
+
+ public static List<Position> getPositions(
+ Storage storage, long deviceId, Date from, Date to) throws StorageException {
+ return storage.getObjects(Position.class, new Request(
+ new Columns.All(),
+ new Condition.And(
+ new Condition.Equals("deviceId", deviceId),
+ new Condition.Between("fixTime", "from", from, "to", to)),
+ new Order("fixTime")));
+ }
+
+ public static List<Position> getLatestPositions(Storage storage, long userId) throws StorageException {
+ var devices = storage.getObjects(Device.class, new Request(
+ new Columns.Include("id"),
+ new Condition.Permission(User.class, userId, Device.class)));
+ var deviceIds = devices.stream().map(BaseModel::getId).collect(Collectors.toUnmodifiableSet());
+
+ var positions = storage.getObjects(Position.class, new Request(
+ new Columns.All(), new Condition.LatestPositions()));
+ return positions.stream()
+ .filter(position -> deviceIds.contains(position.getDeviceId()))
+ .collect(Collectors.toList());
+ }
+
+}
diff --git a/src/main/java/org/traccar/helper/model/UserUtil.java b/src/main/java/org/traccar/helper/model/UserUtil.java
new file mode 100644
index 000000000..9f93afeae
--- /dev/null
+++ b/src/main/java/org/traccar/helper/model/UserUtil.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.helper.model;
+
+import org.traccar.model.Server;
+import org.traccar.model.User;
+import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Order;
+import org.traccar.storage.query.Request;
+
+import java.util.TimeZone;
+
+public final class UserUtil {
+
+ private UserUtil() {
+ }
+
+ public static boolean isEmpty(Storage storage) throws StorageException {
+ return storage.getObjects(User.class, new Request(
+ new Columns.Include("id"),
+ new Order("id", false, 1))).isEmpty();
+ }
+
+ public static String getDistanceUnit(Server server, User user) {
+ return lookupStringAttribute(server, user, "distanceUnit", "km");
+ }
+
+ public static String getSpeedUnit(Server server, User user) {
+ return lookupStringAttribute(server, user, "speedUnit", "kn");
+ }
+
+ public static String getVolumeUnit(Server server, User user) {
+ return lookupStringAttribute(server, user, "volumeUnit", "ltr");
+ }
+
+ public static TimeZone getTimezone(Server server, User user) {
+ String timezone = lookupStringAttribute(server, user, "timezone", null);
+ return timezone != null ? TimeZone.getTimeZone(timezone) : TimeZone.getDefault();
+ }
+
+ private static String lookupStringAttribute(Server server, User user, String key, String defaultValue) {
+ String preference;
+ String serverPreference = server.getString(key);
+ String userPreference = user.getString(key);
+ if (server.getForceSettings()) {
+ preference = serverPreference != null ? serverPreference : userPreference;
+ } else {
+ preference = userPreference != null ? userPreference : serverPreference;
+ }
+ return preference != null ? preference : defaultValue;
+ }
+
+}
diff --git a/src/main/java/org/traccar/mail/LogMailManager.java b/src/main/java/org/traccar/mail/LogMailManager.java
new file mode 100644
index 000000000..b6b912d6c
--- /dev/null
+++ b/src/main/java/org/traccar/mail/LogMailManager.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.mail;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.model.User;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeBodyPart;
+
+public class LogMailManager implements MailManager {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(LogMailManager.class);
+
+ @Override
+ public boolean getEmailEnabled() {
+ return true;
+ }
+
+ @Override
+ public void sendMessage(User user, String subject, String body) throws MessagingException {
+ sendMessage(user, 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);
+ }
+
+}
diff --git a/src/main/java/org/traccar/mail/MailManager.java b/src/main/java/org/traccar/mail/MailManager.java
new file mode 100644
index 000000000..69efbed32
--- /dev/null
+++ b/src/main/java/org/traccar/mail/MailManager.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.mail;
+
+import org.traccar.model.User;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeBodyPart;
+
+public interface MailManager {
+
+ boolean getEmailEnabled();
+
+ void sendMessage(User user, String subject, String body) throws MessagingException;
+
+ void sendMessage(User user, String subject, String body, MimeBodyPart attachment) throws MessagingException;
+
+}
diff --git a/src/main/java/org/traccar/mail/SmtpMailManager.java b/src/main/java/org/traccar/mail/SmtpMailManager.java
new file mode 100644
index 000000000..4a0b7048f
--- /dev/null
+++ b/src/main/java/org/traccar/mail/SmtpMailManager.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2018 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.mail;
+
+import org.traccar.config.Config;
+import org.traccar.config.ConfigKey;
+import org.traccar.config.Keys;
+import org.traccar.database.StatisticsManager;
+import org.traccar.model.User;
+import org.traccar.notification.PropertiesProvider;
+
+import javax.mail.BodyPart;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.Multipart;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
+import java.io.UnsupportedEncodingException;
+import java.util.Date;
+import java.util.Properties;
+
+public final class SmtpMailManager implements MailManager {
+
+ private static final String CONTENT_TYPE = "text/html; charset=utf-8";
+
+ private final Config config;
+ private final StatisticsManager statisticsManager;
+
+ public SmtpMailManager(Config config, StatisticsManager statisticsManager) {
+ this.config = config;
+ this.statisticsManager = statisticsManager;
+ }
+
+ private static void copyBooleanProperty(
+ Properties properties, PropertiesProvider provider, ConfigKey<Boolean> key) {
+ Boolean value = provider.getBoolean(key);
+ if (value != null) {
+ properties.put(key.getKey(), String.valueOf(value));
+ }
+ }
+
+ private static void copyStringProperty(
+ Properties properties, PropertiesProvider provider, ConfigKey<String> key) {
+ String value = provider.getString(key);
+ if (value != null) {
+ properties.put(key.getKey(), value);
+ }
+ }
+
+ private static Properties getProperties(PropertiesProvider provider) {
+ String host = provider.getString(Keys.MAIL_SMTP_HOST);
+ if (host != null) {
+ Properties properties = new Properties();
+
+ properties.put(Keys.MAIL_TRANSPORT_PROTOCOL.getKey(), provider.getString(Keys.MAIL_TRANSPORT_PROTOCOL));
+ properties.put(Keys.MAIL_SMTP_HOST.getKey(), host);
+ properties.put(Keys.MAIL_SMTP_PORT.getKey(), String.valueOf(provider.getInteger(Keys.MAIL_SMTP_PORT)));
+
+ copyBooleanProperty(properties, provider, Keys.MAIL_SMTP_STARTTLS_ENABLE);
+ copyBooleanProperty(properties, provider, Keys.MAIL_SMTP_STARTTLS_REQUIRED);
+ copyBooleanProperty(properties, provider, Keys.MAIL_SMTP_SSL_ENABLE);
+ copyStringProperty(properties, provider, Keys.MAIL_SMTP_SSL_TRUST);
+ copyStringProperty(properties, provider, Keys.MAIL_SMTP_SSL_PROTOCOLS);
+ copyStringProperty(properties, provider, Keys.MAIL_SMTP_USERNAME);
+ copyStringProperty(properties, provider, Keys.MAIL_SMTP_PASSWORD);
+ copyStringProperty(properties, provider, Keys.MAIL_SMTP_FROM);
+ copyStringProperty(properties, provider, Keys.MAIL_SMTP_FROM_NAME);
+
+ return properties;
+ }
+ return null;
+ }
+
+ public boolean getEmailEnabled() {
+ return config.hasKey(Keys.MAIL_SMTP_HOST);
+ }
+
+ public void sendMessage(
+ User user, String subject, String body) throws MessagingException {
+ sendMessage(user, subject, body, null);
+ }
+
+ public void sendMessage(
+ User user, 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) {
+ properties = getProperties(new PropertiesProvider(config));
+ }
+ if (properties == null) {
+ throw new MessagingException("No SMTP configuration found");
+ }
+
+ Session session = Session.getInstance(properties);
+
+ MimeMessage message = new MimeMessage(session);
+
+ String from = properties.getProperty(Keys.MAIL_SMTP_FROM.getKey());
+ if (from != null) {
+ String fromName = properties.getProperty(Keys.MAIL_SMTP_FROM_NAME.getKey());
+ if (fromName != null) {
+ try {
+ message.setFrom(new InternetAddress(from, fromName));
+ } catch (UnsupportedEncodingException e) {
+ throw new MessagingException("Email address issue");
+ }
+ } else {
+ message.setFrom(new InternetAddress(from));
+ }
+ }
+
+ message.addRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail()));
+ message.setSubject(subject);
+ message.setSentDate(new Date());
+
+ if (attachment != null) {
+ Multipart multipart = new MimeMultipart();
+
+ BodyPart messageBodyPart = new MimeBodyPart();
+ messageBodyPart.setContent(body, CONTENT_TYPE);
+ multipart.addBodyPart(messageBodyPart);
+ multipart.addBodyPart(attachment);
+
+ message.setContent(multipart);
+ } else {
+ message.setContent(body, CONTENT_TYPE);
+ }
+
+ try (Transport transport = session.getTransport()) {
+ statisticsManager.registerMail();
+ transport.connect(
+ properties.getProperty(Keys.MAIL_SMTP_HOST.getKey()),
+ properties.getProperty(Keys.MAIL_SMTP_USERNAME.getKey()),
+ properties.getProperty(Keys.MAIL_SMTP_PASSWORD.getKey()));
+ transport.sendMessage(message, message.getAllRecipients());
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/model/Attribute.java b/src/main/java/org/traccar/model/Attribute.java
index 45d40b3ec..65f2e3881 100644
--- a/src/main/java/org/traccar/model/Attribute.java
+++ b/src/main/java/org/traccar/model/Attribute.java
@@ -16,6 +16,9 @@
*/
package org.traccar.model;
+import org.traccar.storage.StorageName;
+
+@StorageName("tc_attributes")
public class Attribute extends BaseModel {
private String description;
diff --git a/src/main/java/org/traccar/database/CalendarManager.java b/src/main/java/org/traccar/model/BaseCommand.java
index 44ced1082..f87b8ef65 100644
--- a/src/main/java/org/traccar/database/CalendarManager.java
+++ b/src/main/java/org/traccar/model/BaseCommand.java
@@ -1,6 +1,5 @@
/*
- * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
- * Copyright 2016 - 2017 Andrey Kunitsyn (andrey@traccar.org)
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.database;
+package org.traccar.model;
-import org.traccar.model.Calendar;
+public class BaseCommand extends Message {
-public class CalendarManager extends SimpleObjectManager<Calendar> {
+ private boolean textChannel;
- public CalendarManager(DataManager dataManager) {
- super(dataManager, Calendar.class);
+ public boolean getTextChannel() {
+ return textChannel;
+ }
+
+ public void setTextChannel(boolean textChannel) {
+ this.textChannel = textChannel;
}
}
diff --git a/src/main/java/org/traccar/model/BaseModel.java b/src/main/java/org/traccar/model/BaseModel.java
index 8bdb916e8..acde0f83d 100644
--- a/src/main/java/org/traccar/model/BaseModel.java
+++ b/src/main/java/org/traccar/model/BaseModel.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,11 +20,11 @@ public class BaseModel {
private long id;
- public final long getId() {
+ public long getId() {
return id;
}
- public final void setId(long id) {
+ public void setId(long id) {
this.id = id;
}
diff --git a/src/main/java/org/traccar/model/Calendar.java b/src/main/java/org/traccar/model/Calendar.java
index 1010325b6..c1f98a957 100644
--- a/src/main/java/org/traccar/model/Calendar.java
+++ b/src/main/java/org/traccar/model/Calendar.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,11 +20,12 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import net.fortuna.ical4j.data.CalendarBuilder;
import net.fortuna.ical4j.data.ParserException;
import net.fortuna.ical4j.filter.Filter;
-import net.fortuna.ical4j.filter.PeriodRule;
+import net.fortuna.ical4j.filter.predicate.PeriodRule;
import net.fortuna.ical4j.model.DateTime;
import net.fortuna.ical4j.model.Period;
import net.fortuna.ical4j.model.component.CalendarComponent;
-import org.traccar.database.QueryIgnore;
+import org.traccar.storage.QueryIgnore;
+import org.traccar.storage.StorageName;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@@ -32,6 +33,7 @@ import java.time.Duration;
import java.util.Collection;
import java.util.Date;
+@StorageName("tc_calendars")
public class Calendar extends ExtendedModel {
private String name;
@@ -47,13 +49,13 @@ public class Calendar extends ExtendedModel {
private byte[] data;
public byte[] getData() {
- return data.clone();
+ return data;
}
public void setData(byte[] data) throws IOException, ParserException {
CalendarBuilder builder = new CalendarBuilder();
calendar = builder.build(new ByteArrayInputStream(data));
- this.data = data.clone();
+ this.data = data;
}
private net.fortuna.ical4j.model.Calendar calendar;
diff --git a/src/main/java/org/traccar/model/CellTower.java b/src/main/java/org/traccar/model/CellTower.java
index 254487471..355594c64 100644
--- a/src/main/java/org/traccar/model/CellTower.java
+++ b/src/main/java/org/traccar/model/CellTower.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,9 +16,11 @@
package org.traccar.model;
import com.fasterxml.jackson.annotation.JsonInclude;
-import org.traccar.Context;
+import org.traccar.config.Config;
import org.traccar.config.Keys;
+import java.util.Objects;
+
@JsonInclude(JsonInclude.Include.NON_NULL)
public class CellTower {
@@ -37,14 +39,12 @@ public class CellTower {
return cellTower;
}
- public static CellTower fromLacCid(int lac, long cid) {
- return from(
- Context.getConfig().getInteger(Keys.GEOLOCATION_MCC),
- Context.getConfig().getInteger(Keys.GEOLOCATION_MCC), lac, cid);
+ public static CellTower fromLacCid(Config config, int lac, long cid) {
+ return from(config.getInteger(Keys.GEOLOCATION_MCC), config.getInteger(Keys.GEOLOCATION_MNC), lac, cid);
}
- public static CellTower fromCidLac(long cid, int lac) {
- return fromLacCid(lac, cid);
+ public static CellTower fromCidLac(Config config, long cid, int lac) {
+ return fromLacCid(config, lac, cid);
}
private String radioType;
@@ -113,4 +113,26 @@ public class CellTower {
mobileNetworkCode = Integer.parseInt(operatorString.substring(3));
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ CellTower cellTower = (CellTower) o;
+ return Objects.equals(radioType, cellTower.radioType)
+ && Objects.equals(cellId, cellTower.cellId)
+ && Objects.equals(locationAreaCode, cellTower.locationAreaCode)
+ && Objects.equals(mobileCountryCode, cellTower.mobileCountryCode)
+ && Objects.equals(mobileNetworkCode, cellTower.mobileNetworkCode)
+ && Objects.equals(signalStrength, cellTower.signalStrength);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(radioType, cellId, locationAreaCode, mobileCountryCode, mobileNetworkCode, signalStrength);
+ }
+
}
diff --git a/src/main/java/org/traccar/model/Command.java b/src/main/java/org/traccar/model/Command.java
index 99930d1e6..99988dd82 100644
--- a/src/main/java/org/traccar/model/Command.java
+++ b/src/main/java/org/traccar/model/Command.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,12 +15,14 @@
*/
package org.traccar.model;
-import org.traccar.database.QueryIgnore;
+import org.traccar.storage.QueryIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import org.traccar.storage.StorageName;
+@StorageName("tc_commands")
@JsonIgnoreProperties(ignoreUnknown = true)
-public class Command extends Message implements Cloneable {
+public class Command extends BaseCommand {
public static final String TYPE_CUSTOM = "custom";
public static final String TYPE_IDENTIFICATION = "deviceIdentification";
@@ -56,11 +58,10 @@ public class Command extends Message implements Cloneable {
public static final String TYPE_GET_MODEM_STATUS = "getModemStatus";
public static final String TYPE_GET_DEVICE_STATUS = "getDeviceStatus";
public static final String TYPE_SET_SPEED_LIMIT = "setSpeedLimit";
-
public static final String TYPE_MODE_POWER_SAVING = "modePowerSaving";
public static final String TYPE_MODE_DEEP_SLEEP = "modeDeepSleep";
- public static final String TYPE_ALARM_GEOFENCE = "movementAlarm";
+ public static final String TYPE_ALARM_GEOFENCE = "alarmGeofence";
public static final String TYPE_ALARM_BATTERY = "alarmBattery";
public static final String TYPE_ALARM_SOS = "alarmSos";
public static final String TYPE_ALARM_REMOVE = "alarmRemove";
@@ -83,25 +84,16 @@ public class Command extends Message implements Cloneable {
public static final String KEY_SERVER = "server";
public static final String KEY_PORT = "port";
+ @QueryIgnore
@Override
- public Command clone() throws CloneNotSupportedException {
- return (Command) super.clone();
- }
-
- private boolean textChannel;
-
- public boolean getTextChannel() {
- return textChannel;
- }
-
- public void setTextChannel(boolean textChannel) {
- this.textChannel = textChannel;
+ public long getDeviceId() {
+ return super.getDeviceId();
}
@QueryIgnore
@Override
- public long getDeviceId() {
- return super.getDeviceId();
+ public void setDeviceId(long deviceId) {
+ super.setDeviceId(deviceId);
}
private String description;
diff --git a/src/main/java/org/traccar/model/Device.java b/src/main/java/org/traccar/model/Device.java
index 0c9be932d..b8c87921d 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 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,11 +17,14 @@ package org.traccar.model;
import java.util.Date;
import java.util.List;
+import java.util.stream.Collectors;
-import org.traccar.database.QueryExtended;
-import org.traccar.database.QueryIgnore;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import org.traccar.storage.QueryIgnore;
+import org.traccar.storage.StorageName;
-public class Device extends GroupedModel {
+@StorageName("tc_devices")
+public class Device extends GroupedModel implements Disableable {
private String name;
@@ -55,26 +58,18 @@ public class Device extends GroupedModel {
}
public void setStatus(String status) {
- this.status = status;
+ this.status = status != null ? status.trim() : null;
}
private Date lastUpdate;
- @QueryExtended
+ @QueryIgnore
public Date getLastUpdate() {
- if (lastUpdate != null) {
- return new Date(lastUpdate.getTime());
- } else {
- return null;
- }
+ return this.lastUpdate;
}
public void setLastUpdate(Date lastUpdate) {
- if (lastUpdate != null) {
- this.lastUpdate = new Date(lastUpdate.getTime());
- } else {
- this.lastUpdate = null;
- }
+ this.lastUpdate = lastUpdate;
}
private long positionId;
@@ -95,8 +90,12 @@ public class Device extends GroupedModel {
return geofenceIds;
}
- public void setGeofenceIds(List<Long> geofenceIds) {
- this.geofenceIds = 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;
@@ -141,12 +140,117 @@ public class Device extends GroupedModel {
private boolean disabled;
+ @Override
public boolean getDisabled() {
return disabled;
}
+ @Override
public void setDisabled(boolean disabled) {
this.disabled = disabled;
}
+ private Date expirationTime;
+
+ @Override
+ public Date getExpirationTime() {
+ return expirationTime;
+ }
+
+ @Override
+ public void setExpirationTime(Date expirationTime) {
+ this.expirationTime = expirationTime;
+ }
+
+ private boolean motionStreak;
+
+ @QueryIgnore
+ @JsonIgnore
+ public boolean getMotionStreak() {
+ return motionStreak;
+ }
+
+ @JsonIgnore
+ public void setMotionStreak(boolean motionStreak) {
+ this.motionStreak = motionStreak;
+ }
+
+ private boolean motionState;
+
+ @QueryIgnore
+ @JsonIgnore
+ public boolean getMotionState() {
+ return motionState;
+ }
+
+ @JsonIgnore
+ public void setMotionState(boolean motionState) {
+ this.motionState = motionState;
+ }
+
+ private Date motionTime;
+
+ @QueryIgnore
+ @JsonIgnore
+ public Date getMotionTime() {
+ return motionTime;
+ }
+
+ @JsonIgnore
+ public void setMotionTime(Date motionTime) {
+ this.motionTime = motionTime;
+ }
+
+ private double motionDistance;
+
+ @QueryIgnore
+ @JsonIgnore
+ public double getMotionDistance() {
+ return motionDistance;
+ }
+
+ @JsonIgnore
+ public void setMotionDistance(double motionDistance) {
+ this.motionDistance = motionDistance;
+ }
+
+ private boolean overspeedState;
+
+ @QueryIgnore
+ @JsonIgnore
+ public boolean getOverspeedState() {
+ return overspeedState;
+ }
+
+ @JsonIgnore
+ public void setOverspeedState(boolean overspeedState) {
+ this.overspeedState = overspeedState;
+ }
+
+ private Date overspeedTime;
+
+ @QueryIgnore
+ @JsonIgnore
+ public Date getOverspeedTime() {
+ return overspeedTime;
+ }
+
+ @JsonIgnore
+ public void setOverspeedTime(Date overspeedTime) {
+ this.overspeedTime = overspeedTime;
+ }
+
+ private long overspeedGeofenceId;
+
+ @QueryIgnore
+ @JsonIgnore
+ public long getOverspeedGeofenceId() {
+ return overspeedGeofenceId;
+ }
+
+ @JsonIgnore
+ public void setOverspeedGeofenceId(long overspeedGeofenceId) {
+ this.overspeedGeofenceId = overspeedGeofenceId;
+ }
+
}
diff --git a/src/main/java/org/traccar/model/DeviceState.java b/src/main/java/org/traccar/model/DeviceState.java
deleted file mode 100644
index 75d6726ee..000000000
--- a/src/main/java/org/traccar/model/DeviceState.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
- * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.model;
-
-public class DeviceState {
-
- private Boolean motionState;
-
- public void setMotionState(boolean motionState) {
- this.motionState = motionState;
- }
-
- public Boolean getMotionState() {
- return motionState;
- }
-
- private Position motionPosition;
-
- public void setMotionPosition(Position motionPosition) {
- this.motionPosition = motionPosition;
- }
-
- public Position getMotionPosition() {
- return motionPosition;
- }
-
- private Boolean overspeedState;
-
- public void setOverspeedState(boolean overspeedState) {
- this.overspeedState = overspeedState;
- }
-
- public Boolean getOverspeedState() {
- return overspeedState;
- }
-
- private Position overspeedPosition;
-
- public void setOverspeedPosition(Position overspeedPosition) {
- this.overspeedPosition = overspeedPosition;
- }
-
- public Position getOverspeedPosition() {
- return overspeedPosition;
- }
-
- private long overspeedGeofenceId;
-
- public void setOverspeedGeofenceId(long overspeedGeofenceId) {
- this.overspeedGeofenceId = overspeedGeofenceId;
- }
-
- public long getOverspeedGeofenceId() {
- return overspeedGeofenceId;
- }
-
-}
diff --git a/src/main/java/org/traccar/model/Disableable.java b/src/main/java/org/traccar/model/Disableable.java
new file mode 100644
index 000000000..1145d6279
--- /dev/null
+++ b/src/main/java/org/traccar/model/Disableable.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.model;
+
+import java.util.Date;
+
+public interface Disableable {
+
+ boolean getDisabled();
+
+ void setDisabled(boolean disabled);
+
+ Date getExpirationTime();
+
+ void setExpirationTime(Date expirationTime);
+
+ default void checkDisabled() throws SecurityException {
+ if (getDisabled()) {
+ throw new SecurityException(getClass().getSimpleName() + " is disabled");
+ }
+ if (getExpirationTime() != null && System.currentTimeMillis() > getExpirationTime().getTime()) {
+ throw new SecurityException(getClass().getSimpleName() + " has expired");
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/model/Driver.java b/src/main/java/org/traccar/model/Driver.java
index 05f52fd4d..b9e023088 100644
--- a/src/main/java/org/traccar/model/Driver.java
+++ b/src/main/java/org/traccar/model/Driver.java
@@ -16,6 +16,9 @@
*/
package org.traccar.model;
+import org.traccar.storage.StorageName;
+
+@StorageName("tc_drivers")
public class Driver extends ExtendedModel {
private String name;
@@ -37,4 +40,5 @@ public class Driver extends ExtendedModel {
public void setUniqueId(String uniqueId) {
this.uniqueId = uniqueId;
}
+
}
diff --git a/src/main/java/org/traccar/model/Event.java b/src/main/java/org/traccar/model/Event.java
index a7a134ecf..0e851d748 100644
--- a/src/main/java/org/traccar/model/Event.java
+++ b/src/main/java/org/traccar/model/Event.java
@@ -15,8 +15,11 @@
*/
package org.traccar.model;
+import org.traccar.storage.StorageName;
+
import java.util.Date;
+@StorageName("tc_events")
public class Event extends Message {
public Event(String type, Position position) {
@@ -49,6 +52,7 @@ public class Event extends Message {
public static final String TYPE_DEVICE_OVERSPEED = "deviceOverspeed";
public static final String TYPE_DEVICE_FUEL_DROP = "deviceFuelDrop";
+ public static final String TYPE_DEVICE_FUEL_INCREASE = "deviceFuelIncrease";
public static final String TYPE_GEOFENCE_ENTER = "geofenceEnter";
public static final String TYPE_GEOFENCE_EXIT = "geofenceExit";
@@ -59,10 +63,9 @@ public class Event extends Message {
public static final String TYPE_IGNITION_OFF = "ignitionOff";
public static final String TYPE_MAINTENANCE = "maintenance";
-
public static final String TYPE_TEXT_MESSAGE = "textMessage";
-
public static final String TYPE_DRIVER_CHANGED = "driverChanged";
+ public static final String TYPE_MEDIA = "media";
private Date eventTime;
diff --git a/src/main/java/org/traccar/model/ExtendedModel.java b/src/main/java/org/traccar/model/ExtendedModel.java
index 8353d0e66..7a61eda8c 100644
--- a/src/main/java/org/traccar/model/ExtendedModel.java
+++ b/src/main/java/org/traccar/model/ExtendedModel.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,17 +17,22 @@ package org.traccar.model;
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.Objects;
public class ExtendedModel extends BaseModel {
private Map<String, Object> attributes = new LinkedHashMap<>();
+ public boolean hasAttribute(String key) {
+ return attributes.containsKey(key);
+ }
+
public Map<String, Object> getAttributes() {
return attributes;
}
public void setAttributes(Map<String, Object> attributes) {
- this.attributes = attributes;
+ this.attributes = Objects.requireNonNullElseGet(attributes, LinkedHashMap::new);
}
public void set(String key, Boolean value) {
@@ -86,7 +91,7 @@ public class ExtendedModel extends BaseModel {
public String getString(String key) {
if (attributes.containsKey(key)) {
- return (String) attributes.get(key);
+ return attributes.get(key).toString();
} else {
return null;
}
@@ -94,7 +99,12 @@ public class ExtendedModel extends BaseModel {
public double getDouble(String key) {
if (attributes.containsKey(key)) {
- return ((Number) attributes.get(key)).doubleValue();
+ Object value = attributes.get(key);
+ if (value instanceof Number) {
+ return ((Number) attributes.get(key)).doubleValue();
+ } else {
+ return Double.parseDouble(value.toString());
+ }
} else {
return 0.0;
}
@@ -102,7 +112,12 @@ public class ExtendedModel extends BaseModel {
public boolean getBoolean(String key) {
if (attributes.containsKey(key)) {
- return (Boolean) attributes.get(key);
+ Object value = attributes.get(key);
+ if (value instanceof Boolean) {
+ return (Boolean) attributes.get(key);
+ } else {
+ return Boolean.parseBoolean(value.toString());
+ }
} else {
return false;
}
@@ -110,7 +125,12 @@ public class ExtendedModel extends BaseModel {
public int getInteger(String key) {
if (attributes.containsKey(key)) {
- return ((Number) attributes.get(key)).intValue();
+ Object value = attributes.get(key);
+ if (value instanceof Number) {
+ return ((Number) attributes.get(key)).intValue();
+ } else {
+ return Integer.parseInt(value.toString());
+ }
} else {
return 0;
}
@@ -118,7 +138,12 @@ public class ExtendedModel extends BaseModel {
public long getLong(String key) {
if (attributes.containsKey(key)) {
- return ((Number) attributes.get(key)).longValue();
+ Object value = attributes.get(key);
+ if (value instanceof Number) {
+ return ((Number) attributes.get(key)).longValue();
+ } else {
+ return Long.parseLong(value.toString());
+ }
} else {
return 0;
}
diff --git a/src/main/java/org/traccar/model/Geofence.java b/src/main/java/org/traccar/model/Geofence.java
index 85f392f99..9259028fb 100644
--- a/src/main/java/org/traccar/model/Geofence.java
+++ b/src/main/java/org/traccar/model/Geofence.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,24 +15,19 @@
*/
package org.traccar.model;
-import java.text.ParseException;
-
-import org.traccar.Context;
-import org.traccar.config.Keys;
-import org.traccar.database.QueryIgnore;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import org.traccar.geofence.GeofenceCircle;
import org.traccar.geofence.GeofenceGeometry;
import org.traccar.geofence.GeofencePolygon;
import org.traccar.geofence.GeofencePolyline;
+import org.traccar.storage.QueryIgnore;
+import org.traccar.storage.StorageName;
-import com.fasterxml.jackson.annotation.JsonIgnore;
+import java.text.ParseException;
+@StorageName("tc_geofences")
public class Geofence extends ScheduledModel {
- public static final String TYPE_GEOFENCE_CILCLE = "geofenceCircle";
- public static final String TYPE_GEOFENCE_POLYGON = "geofencePolygon";
- public static final String TYPE_GEOFENCE_POLYLINE = "geofencePolyline";
-
private String name;
public String getName() {
@@ -66,9 +61,7 @@ public class Geofence extends ScheduledModel {
} else if (area.startsWith("POLYGON")) {
geometry = new GeofencePolygon(area);
} else if (area.startsWith("LINESTRING")) {
- final double distance = getDouble("polylineDistance");
- geometry = new GeofencePolyline(area, distance > 0 ? distance
- : Context.getConfig().getDouble(Keys.GEOFENCE_POLYLINE_DISTANCE));
+ geometry = new GeofencePolyline(area);
} else {
throw new ParseException("Unknown geometry type", 0);
}
@@ -90,4 +83,5 @@ public class Geofence extends ScheduledModel {
area = geometry.toWkt();
this.geometry = geometry;
}
+
}
diff --git a/src/main/java/org/traccar/model/Group.java b/src/main/java/org/traccar/model/Group.java
index 91ea2319d..ff69f61fa 100644
--- a/src/main/java/org/traccar/model/Group.java
+++ b/src/main/java/org/traccar/model/Group.java
@@ -15,6 +15,9 @@
*/
package org.traccar.model;
+import org.traccar.storage.StorageName;
+
+@StorageName("tc_groups")
public class Group extends GroupedModel {
private String name;
diff --git a/src/main/java/org/traccar/model/Maintenance.java b/src/main/java/org/traccar/model/Maintenance.java
index 73f67ea96..cad100a3a 100644
--- a/src/main/java/org/traccar/model/Maintenance.java
+++ b/src/main/java/org/traccar/model/Maintenance.java
@@ -16,6 +16,9 @@
*/
package org.traccar.model;
+import org.traccar.storage.StorageName;
+
+@StorageName("tc_maintenances")
public class Maintenance extends ExtendedModel {
private String name;
diff --git a/src/main/java/org/traccar/model/Network.java b/src/main/java/org/traccar/model/Network.java
index 4d67fc5d8..b31c53c38 100644
--- a/src/main/java/org/traccar/model/Network.java
+++ b/src/main/java/org/traccar/model/Network.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonInclude;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Objects;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Network {
@@ -118,4 +119,27 @@ public class Network {
wifiAccessPoints.add(wifiAccessPoint);
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Network network = (Network) o;
+ return Objects.equals(homeMobileCountryCode, network.homeMobileCountryCode)
+ && Objects.equals(homeMobileNetworkCode, network.homeMobileNetworkCode)
+ && Objects.equals(radioType, network.radioType)
+ && Objects.equals(carrier, network.carrier)
+ && Objects.equals(cellTowers, network.cellTowers)
+ && Objects.equals(wifiAccessPoints, network.wifiAccessPoints);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ homeMobileCountryCode, homeMobileNetworkCode, radioType, carrier, cellTowers, wifiAccessPoints);
+ }
+
}
diff --git a/src/main/java/org/traccar/model/Notification.java b/src/main/java/org/traccar/model/Notification.java
index f1983a03a..95e446132 100644
--- a/src/main/java/org/traccar/model/Notification.java
+++ b/src/main/java/org/traccar/model/Notification.java
@@ -18,10 +18,12 @@ package org.traccar.model;
import java.util.HashSet;
import java.util.Set;
-import org.traccar.database.QueryIgnore;
+import org.traccar.storage.QueryIgnore;
import com.fasterxml.jackson.annotation.JsonIgnore;
+import org.traccar.storage.StorageName;
+@StorageName("tc_notifications")
public class Notification extends ScheduledModel {
private boolean always;
@@ -44,7 +46,6 @@ public class Notification extends ScheduledModel {
this.type = type;
}
-
private String notificators;
public String getNotificators() {
@@ -55,7 +56,6 @@ public class Notification extends ScheduledModel {
this.notificators = transports;
}
-
@JsonIgnore
@QueryIgnore
public Set<String> getNotificatorsTypes() {
diff --git a/src/main/java/org/traccar/model/Order.java b/src/main/java/org/traccar/model/Order.java
index fe6d926b8..7d09b0a47 100644
--- a/src/main/java/org/traccar/model/Order.java
+++ b/src/main/java/org/traccar/model/Order.java
@@ -15,6 +15,9 @@
*/
package org.traccar.model;
+import org.traccar.storage.StorageName;
+
+@StorageName("tc_orders")
public class Order extends ExtendedModel {
private String uniqueId;
diff --git a/src/main/java/org/traccar/model/Pair.java b/src/main/java/org/traccar/model/Pair.java
new file mode 100644
index 000000000..b679de57b
--- /dev/null
+++ b/src/main/java/org/traccar/model/Pair.java
@@ -0,0 +1,43 @@
+package org.traccar.model;
+
+import java.util.Objects;
+
+public class Pair<K, V> {
+
+ private final K first;
+ private final V second;
+
+ public Pair(K first, V second) {
+ this.first = first;
+ this.second = second;
+ }
+
+ public K getFirst() {
+ return first;
+ }
+
+ public V getSecond() {
+ return second;
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Pair pair = (Pair) o;
+
+ return Objects.equals(first, pair.first) && Objects.equals(second, pair.second);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(first, second);
+ }
+
+}
diff --git a/src/main/java/org/traccar/model/Permission.java b/src/main/java/org/traccar/model/Permission.java
index 6475a4582..0b2f0584f 100644
--- a/src/main/java/org/traccar/model/Permission.java
+++ b/src/main/java/org/traccar/model/Permission.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,42 +16,121 @@
*/
package org.traccar.model;
-import java.util.Iterator;
+import java.beans.Introspector;
+import java.io.IOException;
+import java.net.URISyntaxException;
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.TreeMap;
-import org.traccar.database.DataManager;
+import com.fasterxml.jackson.annotation.JsonAnyGetter;
+import com.fasterxml.jackson.annotation.JsonAnySetter;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import org.traccar.helper.ClassScanner;
+import org.traccar.storage.QueryIgnore;
public class Permission {
- private final Class<?> ownerClass;
+ private static final Map<String, Class<? extends BaseModel>> CLASSES = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+
+ static {
+ try {
+ for (Class<?> clazz : ClassScanner.findSubclasses(BaseModel.class)) {
+ CLASSES.put(clazz.getSimpleName(), (Class<? extends BaseModel>) clazz);
+ }
+ } catch (IOException | ReflectiveOperationException | URISyntaxException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private final LinkedHashMap<String, Long> data;
+
+ private final Class<? extends BaseModel> ownerClass;
private final long ownerId;
- private final Class<?> propertyClass;
+ private final Class<? extends BaseModel> propertyClass;
private final long propertyId;
- public Permission(LinkedHashMap<String, Long> permissionMap) throws ClassNotFoundException {
- Iterator<Map.Entry<String, Long>> iterator = permissionMap.entrySet().iterator();
- String owner = iterator.next().getKey();
- ownerClass = DataManager.getClassByName(owner);
- String property = iterator.next().getKey();
- propertyClass = DataManager.getClassByName(property);
- ownerId = permissionMap.get(owner);
- propertyId = permissionMap.get(property);
+ public Permission(LinkedHashMap<String, Long> data) {
+ this.data = data;
+ var iterator = data.entrySet().iterator();
+ var owner = iterator.next();
+ ownerClass = getKeyClass(owner.getKey());
+ ownerId = owner.getValue();
+ var property = iterator.next();
+ propertyClass = getKeyClass(property.getKey());
+ propertyId = property.getValue();
+ }
+
+ public Permission(
+ Class<? extends BaseModel> ownerClass, long ownerId,
+ Class<? extends BaseModel> propertyClass, long propertyId) {
+ this.ownerClass = ownerClass;
+ this.ownerId = ownerId;
+ this.propertyClass = propertyClass;
+ this.propertyId = propertyId;
+ data = new LinkedHashMap<>();
+ data.put(getKey(ownerClass), ownerId);
+ data.put(getKey(propertyClass), propertyId);
+ }
+
+ public static Class<? extends BaseModel> getKeyClass(String key) {
+ return CLASSES.get(key.substring(0, key.length() - 2));
+ }
+
+ public static String getKey(Class<?> clazz) {
+ return Introspector.decapitalize(clazz.getSimpleName()) + "Id";
}
- public Class<?> getOwnerClass() {
+ public static String getStorageName(Class<?> ownerClass, Class<?> propertyClass) {
+ String ownerName = ownerClass.getSimpleName();
+ String propertyName = propertyClass.getSimpleName();
+ String managedPrefix = "Managed";
+ if (propertyName.startsWith(managedPrefix)) {
+ propertyName = propertyName.substring(managedPrefix.length());
+ }
+ return "tc_" + Introspector.decapitalize(ownerName) + "_" + Introspector.decapitalize(propertyName);
+ }
+
+ @QueryIgnore
+ @JsonIgnore
+ public String getStorageName() {
+ return getStorageName(ownerClass, propertyClass);
+ }
+
+ @QueryIgnore
+ @JsonAnyGetter
+ public Map<String, Long> get() {
+ return data;
+ }
+
+ @QueryIgnore
+ @JsonAnySetter
+ public void set(String key, Long value) {
+ data.put(key, value);
+ }
+
+ @QueryIgnore
+ @JsonIgnore
+ public Class<? extends BaseModel> getOwnerClass() {
return ownerClass;
}
+ @QueryIgnore
+ @JsonIgnore
public long getOwnerId() {
return ownerId;
}
- public Class<?> getPropertyClass() {
+ @QueryIgnore
+ @JsonIgnore
+ public Class<? extends BaseModel> getPropertyClass() {
return propertyClass;
}
+ @QueryIgnore
+ @JsonIgnore
public long getPropertyId() {
return propertyId;
}
+
}
diff --git a/src/main/java/org/traccar/model/Position.java b/src/main/java/org/traccar/model/Position.java
index 09d25e832..1286db5f2 100644
--- a/src/main/java/org/traccar/model/Position.java
+++ b/src/main/java/org/traccar/model/Position.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,11 @@ package org.traccar.model;
import java.util.Date;
-import org.traccar.database.QueryIgnore;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import org.traccar.storage.QueryIgnore;
+import org.traccar.storage.StorageName;
+@StorageName("tc_positions")
public class Position extends Message {
public static final String KEY_ORIGINAL = "raw";
@@ -147,7 +150,6 @@ public class Position extends Message {
public Position(String protocol) {
this.protocol = protocol;
- this.serverTime = new Date();
}
private String protocol;
@@ -190,6 +192,7 @@ public class Position extends Message {
this.fixTime = fixTime;
}
+ @QueryIgnore
public void setTime(Date time) {
setDeviceTime(time);
setFixTime(time);
@@ -202,6 +205,7 @@ public class Position extends Message {
return outdated;
}
+ @QueryIgnore
public void setOutdated(boolean outdated) {
this.outdated = outdated;
}
@@ -223,6 +227,9 @@ public class Position extends Message {
}
public void setLatitude(double latitude) {
+ if (latitude < -90 || latitude > 90) {
+ throw new IllegalArgumentException("Latitude out of range");
+ }
this.latitude = latitude;
}
@@ -233,6 +240,9 @@ public class Position extends Message {
}
public void setLongitude(double longitude) {
+ if (longitude < -180 || longitude > 180) {
+ throw new IllegalArgumentException("Longitude out of range");
+ }
this.longitude = longitude;
}
@@ -296,10 +306,18 @@ public class Position extends Message {
this.network = network;
}
- @Override
+ @JsonIgnore
@QueryIgnore
+ @Override
public String getType() {
return super.getType();
}
+ @JsonIgnore
+ @QueryIgnore
+ @Override
+ public void setType(String type) {
+ super.setType(type);
+ }
+
}
diff --git a/src/main/java/org/traccar/model/QueuedCommand.java b/src/main/java/org/traccar/model/QueuedCommand.java
new file mode 100644
index 000000000..96a1eca4b
--- /dev/null
+++ b/src/main/java/org/traccar/model/QueuedCommand.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import org.traccar.storage.StorageName;
+
+import java.util.HashMap;
+
+@StorageName("tc_commands_queue")
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class QueuedCommand extends BaseCommand {
+
+ public static QueuedCommand fromCommand(Command command) {
+ QueuedCommand queuedCommand = new QueuedCommand();
+ queuedCommand.setDeviceId(command.getDeviceId());
+ queuedCommand.setType(command.getType());
+ queuedCommand.setTextChannel(command.getTextChannel());
+ queuedCommand.setAttributes(new HashMap<>(command.getAttributes()));
+ return queuedCommand;
+ }
+
+ public Command toCommand() {
+ Command command = new Command();
+ command.setDeviceId(getDeviceId());
+ command.setType(getType());
+ command.setDescription("");
+ command.setTextChannel(getTextChannel());
+ command.setAttributes(new HashMap<>(getAttributes()));
+ return command;
+ }
+
+}
diff --git a/src/main/java/org/traccar/model/Server.java b/src/main/java/org/traccar/model/Server.java
index 7bdb53b22..73645721b 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 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,11 +16,12 @@
package org.traccar.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import org.traccar.Context;
-import org.traccar.database.QueryIgnore;
+import org.traccar.storage.QueryIgnore;
+import org.traccar.storage.StorageName;
+@StorageName("tc_servers")
@JsonIgnoreProperties(ignoreUnknown = true)
-public class Server extends ExtendedModel {
+public class Server extends ExtendedModel implements UserRestrictions {
private boolean registration;
@@ -34,6 +35,7 @@ public class Server extends ExtendedModel {
private boolean readonly;
+ @Override
public boolean getReadonly() {
return readonly;
}
@@ -44,6 +46,7 @@ public class Server extends ExtendedModel {
private boolean deviceReadonly;
+ @Override
public boolean getDeviceReadonly() {
return deviceReadonly;
}
@@ -82,6 +85,16 @@ public class Server extends ExtendedModel {
this.mapUrl = mapUrl;
}
+ private String overlayUrl;
+
+ public String getOverlayUrl() {
+ return overlayUrl;
+ }
+
+ public void setOverlayUrl(String overlayUrl) {
+ this.overlayUrl = overlayUrl;
+ }
+
private double latitude;
public double getLatitude() {
@@ -144,6 +157,7 @@ public class Server extends ExtendedModel {
private boolean limitCommands;
+ @Override
public boolean getLimitCommands() {
return limitCommands;
}
@@ -152,6 +166,28 @@ public class Server extends ExtendedModel {
this.limitCommands = limitCommands;
}
+ private boolean disableReports;
+
+ @Override
+ public boolean getDisableReports() {
+ return disableReports;
+ }
+
+ public void setDisableReports(boolean disableReports) {
+ this.disableReports = disableReports;
+ }
+
+ private boolean fixedEmail;
+
+ @Override
+ public boolean getFixedEmail() {
+ return fixedEmail;
+ }
+
+ public void setFixedEmail(boolean fixedEmail) {
+ this.fixedEmail = fixedEmail;
+ }
+
private String poiLayer;
public String getPoiLayer() {
@@ -177,9 +213,52 @@ public class Server extends ExtendedModel {
return getClass().getPackage().getImplementationVersion();
}
+ private boolean emailEnabled;
+
+ @QueryIgnore
+ public void setEmailEnabled(boolean emailEnabled) {
+ this.emailEnabled = emailEnabled;
+ }
+
@QueryIgnore
public Boolean getEmailEnabled() {
- return Context.getMailManager().getEmailEnabled();
+ return emailEnabled;
+ }
+
+ private boolean geocoderEnabled;
+
+ @QueryIgnore
+ public void setGeocoderEnabled(boolean geocoderEnabled) {
+ this.geocoderEnabled = geocoderEnabled;
+ }
+
+ @QueryIgnore
+ public boolean getGeocoderEnabled() {
+ return geocoderEnabled;
+ }
+
+ private long[] storageSpace;
+
+ @QueryIgnore
+ public long[] getStorageSpace() {
+ return storageSpace;
+ }
+
+ @QueryIgnore
+ public void setStorageSpace(long[] storageSpace) {
+ this.storageSpace = storageSpace;
+ }
+
+ private boolean newServer;
+
+ @QueryIgnore
+ public boolean getNewServer() {
+ return newServer;
+ }
+
+ @QueryIgnore
+ public void setNewServer(boolean newServer) {
+ this.newServer = newServer;
}
}
diff --git a/src/main/java/org/traccar/model/Statistics.java b/src/main/java/org/traccar/model/Statistics.java
index a9a117aef..0dc1b98e8 100644
--- a/src/main/java/org/traccar/model/Statistics.java
+++ b/src/main/java/org/traccar/model/Statistics.java
@@ -15,9 +15,12 @@
*/
package org.traccar.model;
+import org.traccar.storage.StorageName;
+
import java.util.Date;
import java.util.Map;
+@StorageName("tc_statistics")
public class Statistics extends ExtendedModel {
private Date captureTime;
diff --git a/src/main/java/org/traccar/model/User.java b/src/main/java/org/traccar/model/User.java
index 976b6aac0..53594fe07 100644
--- a/src/main/java/org/traccar/model/User.java
+++ b/src/main/java/org/traccar/model/User.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,13 +17,14 @@ package org.traccar.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
-import org.traccar.database.QueryExtended;
-import org.traccar.database.QueryIgnore;
+import org.traccar.storage.QueryIgnore;
import org.traccar.helper.Hashing;
+import org.traccar.storage.StorageName;
import java.util.Date;
-public class User extends ExtendedModel {
+@StorageName("tc_users")
+public class User extends ExtendedModel implements UserRestrictions, Disableable {
private String name;
@@ -67,6 +68,7 @@ public class User extends ExtendedModel {
private boolean readonly;
+ @Override
public boolean getReadonly() {
return readonly;
}
@@ -77,6 +79,12 @@ public class User extends ExtendedModel {
private boolean administrator;
+ @QueryIgnore
+ @JsonIgnore
+ public boolean getManager() {
+ return userLimit != 0;
+ }
+
public boolean getAdministrator() {
return administrator;
}
@@ -147,20 +155,24 @@ public class User extends ExtendedModel {
private boolean disabled;
+ @Override
public boolean getDisabled() {
return disabled;
}
+ @Override
public void setDisabled(boolean disabled) {
this.disabled = disabled;
}
private Date expirationTime;
+ @Override
public Date getExpirationTime() {
return expirationTime;
}
+ @Override
public void setExpirationTime(Date expirationTime) {
this.expirationTime = expirationTime;
}
@@ -187,6 +199,7 @@ public class User extends ExtendedModel {
private boolean deviceReadonly;
+ @Override
public boolean getDeviceReadonly() {
return deviceReadonly;
}
@@ -195,25 +208,9 @@ public class User extends ExtendedModel {
this.deviceReadonly = deviceReadonly;
}
- private String token;
-
- public String getToken() {
- return token;
- }
-
- public void setToken(String token) {
- if (token != null && !token.isEmpty()) {
- if (!token.matches("^[a-zA-Z0-9-]{16,}$")) {
- throw new IllegalArgumentException("Illegal token");
- }
- this.token = token;
- } else {
- this.token = null;
- }
- }
-
private boolean limitCommands;
+ @Override
public boolean getLimitCommands() {
return limitCommands;
}
@@ -222,6 +219,28 @@ public class User extends ExtendedModel {
this.limitCommands = limitCommands;
}
+ private boolean disableReports;
+
+ @Override
+ public boolean getDisableReports() {
+ return disableReports;
+ }
+
+ public void setDisableReports(boolean disableReports) {
+ this.disableReports = disableReports;
+ }
+
+ private boolean fixedEmail;
+
+ @Override
+ public boolean getFixedEmail() {
+ return fixedEmail;
+ }
+
+ public void setFixedEmail(boolean fixedEmail) {
+ this.fixedEmail = fixedEmail;
+ }
+
private String poiLayer;
public String getPoiLayer() {
@@ -237,6 +256,7 @@ public class User extends ExtendedModel {
return null;
}
+ @QueryIgnore
public void setPassword(String password) {
if (password != null && !password.isEmpty()) {
Hashing.HashingResult hashingResult = Hashing.createHash(password);
@@ -248,11 +268,12 @@ public class User extends ExtendedModel {
private String hashedPassword;
@JsonIgnore
- @QueryExtended
+ @QueryIgnore
public String getHashedPassword() {
return hashedPassword;
}
+ @QueryIgnore
public void setHashedPassword(String hashedPassword) {
this.hashedPassword = hashedPassword;
}
@@ -260,11 +281,12 @@ public class User extends ExtendedModel {
private String salt;
@JsonIgnore
- @QueryExtended
+ @QueryIgnore
public String getSalt() {
return salt;
}
+ @QueryIgnore
public void setSalt(String salt) {
this.salt = salt;
}
diff --git a/src/main/java/org/traccar/model/UserRestrictions.java b/src/main/java/org/traccar/model/UserRestrictions.java
new file mode 100644
index 000000000..1fcc5682e
--- /dev/null
+++ b/src/main/java/org/traccar/model/UserRestrictions.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.model;
+
+public interface UserRestrictions {
+ boolean getReadonly();
+ boolean getDeviceReadonly();
+ boolean getLimitCommands();
+ boolean getDisableReports();
+ boolean getFixedEmail();
+}
diff --git a/src/main/java/org/traccar/model/WifiAccessPoint.java b/src/main/java/org/traccar/model/WifiAccessPoint.java
index 87a77f3c0..e28c1b935 100644
--- a/src/main/java/org/traccar/model/WifiAccessPoint.java
+++ b/src/main/java/org/traccar/model/WifiAccessPoint.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,8 @@ package org.traccar.model;
import com.fasterxml.jackson.annotation.JsonInclude;
+import java.util.Objects;
+
@JsonInclude(JsonInclude.Include.NON_NULL)
public class WifiAccessPoint {
@@ -63,4 +65,23 @@ public class WifiAccessPoint {
this.channel = channel;
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ WifiAccessPoint that = (WifiAccessPoint) o;
+ return Objects.equals(macAddress, that.macAddress)
+ && Objects.equals(signalStrength, that.signalStrength)
+ && Objects.equals(channel, that.channel);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(macAddress, signalStrength, channel);
+ }
+
}
diff --git a/src/main/java/org/traccar/notification/EventForwarder.java b/src/main/java/org/traccar/notification/EventForwarder.java
deleted file mode 100644
index c908fedbd..000000000
--- a/src/main/java/org/traccar/notification/EventForwarder.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.notification;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.traccar.Context;
-import org.traccar.config.Keys;
-import org.traccar.model.Device;
-import org.traccar.model.Event;
-import org.traccar.model.Geofence;
-import org.traccar.model.Maintenance;
-import org.traccar.model.Position;
-
-import javax.ws.rs.client.Entity;
-import javax.ws.rs.client.Invocation;
-import javax.ws.rs.client.InvocationCallback;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
-public class EventForwarder {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(EventForwarder.class);
-
- private final String url;
- private final String header;
-
- public EventForwarder() {
- url = Context.getConfig().getString(Keys.EVENT_FORWARD_URL);
- header = Context.getConfig().getString(Keys.EVENT_FORWARD_HEADERS);
- }
-
- private static final String KEY_POSITION = "position";
- private static final String KEY_EVENT = "event";
- private static final String KEY_GEOFENCE = "geofence";
- private static final String KEY_DEVICE = "device";
- private static final String KEY_MAINTENANCE = "maintenance";
- private static final String KEY_USERS = "users";
-
- public final void forwardEvent(Event event, Position position, Set<Long> users) {
-
- Invocation.Builder requestBuilder = Context.getClient().target(url).request();
-
- if (header != null && !header.isEmpty()) {
- for (String line: header.split("\\r?\\n")) {
- String[] values = line.split(":", 2);
- requestBuilder.header(values[0].trim(), values[1].trim());
- }
- }
-
- LOGGER.debug("Event forwarding initiated");
- requestBuilder.async().post(
- Entity.json(preparePayload(event, position, users)), new InvocationCallback<Object>() {
- @Override
- public void completed(Object o) {
- LOGGER.debug("Event forwarding succeeded");
- }
-
- @Override
- public void failed(Throwable throwable) {
- LOGGER.warn("Event forwarding failed", throwable);
- }
- });
- }
-
- protected Map<String, Object> preparePayload(Event event, Position position, Set<Long> users) {
- Map<String, Object> data = new HashMap<>();
- data.put(KEY_EVENT, event);
- if (position != null) {
- data.put(KEY_POSITION, position);
- }
- Device device = Context.getIdentityManager().getById(event.getDeviceId());
- if (device != null) {
- data.put(KEY_DEVICE, device);
- }
- if (event.getGeofenceId() != 0) {
- Geofence geofence = Context.getGeofenceManager().getById(event.getGeofenceId());
- if (geofence != null) {
- data.put(KEY_GEOFENCE, geofence);
- }
- }
- if (event.getMaintenanceId() != 0) {
- Maintenance maintenance = Context.getMaintenancesManager().getById(event.getMaintenanceId());
- if (maintenance != null) {
- data.put(KEY_MAINTENANCE, maintenance);
- }
- }
- data.put(KEY_USERS, Context.getUsersManager().getItems(users));
- return data;
- }
-
-}
diff --git a/src/main/java/org/traccar/notification/NotificationFormatter.java b/src/main/java/org/traccar/notification/NotificationFormatter.java
index 9a6723a71..9ee3b97b6 100644
--- a/src/main/java/org/traccar/notification/NotificationFormatter.java
+++ b/src/main/java/org/traccar/notification/NotificationFormatter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2017 - 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,50 +17,59 @@
package org.traccar.notification;
import org.apache.velocity.VelocityContext;
-import org.traccar.Context;
+import org.traccar.helper.model.UserUtil;
import org.traccar.model.Device;
import org.traccar.model.Event;
+import org.traccar.model.Geofence;
+import org.traccar.model.Maintenance;
import org.traccar.model.Position;
+import org.traccar.model.Server;
import org.traccar.model.User;
-import org.traccar.reports.ReportUtils;
+import org.traccar.session.cache.CacheManager;
-public final class NotificationFormatter {
+import javax.inject.Inject;
+import javax.inject.Singleton;
- private NotificationFormatter() {
+@Singleton
+public class NotificationFormatter {
+
+ private final CacheManager cacheManager;
+ private final TextTemplateFormatter textTemplateFormatter;
+
+ @Inject
+ public NotificationFormatter(
+ CacheManager cacheManager, TextTemplateFormatter textTemplateFormatter) {
+ this.cacheManager = cacheManager;
+ this.textTemplateFormatter = textTemplateFormatter;
}
- public static VelocityContext prepareContext(long userId, Event event, Position position) {
+ public NotificationMessage formatMessage(User user, Event event, Position position, String templatePath) {
- User user = Context.getPermissionsManager().getUser(userId);
- Device device = Context.getIdentityManager().getById(event.getDeviceId());
+ Server server = cacheManager.getServer();
+ Device device = cacheManager.getObject(Device.class, event.getDeviceId());
- VelocityContext velocityContext = TextTemplateFormatter.prepareContext(user);
+ VelocityContext velocityContext = textTemplateFormatter.prepareContext(server, user);
velocityContext.put("device", device);
velocityContext.put("event", event);
if (position != null) {
velocityContext.put("position", position);
- velocityContext.put("speedUnit", ReportUtils.getSpeedUnit(userId));
- velocityContext.put("distanceUnit", ReportUtils.getDistanceUnit(userId));
- velocityContext.put("volumeUnit", ReportUtils.getVolumeUnit(userId));
+ velocityContext.put("speedUnit", UserUtil.getSpeedUnit(server, user));
+ velocityContext.put("distanceUnit", UserUtil.getDistanceUnit(server, user));
+ velocityContext.put("volumeUnit", UserUtil.getVolumeUnit(server, user));
}
if (event.getGeofenceId() != 0) {
- velocityContext.put("geofence", Context.getGeofenceManager().getById(event.getGeofenceId()));
+ velocityContext.put("geofence", cacheManager.getObject(Geofence.class, event.getGeofenceId()));
}
if (event.getMaintenanceId() != 0) {
- velocityContext.put("maintenance", Context.getMaintenancesManager().getById(event.getMaintenanceId()));
+ velocityContext.put("maintenance", cacheManager.getObject(Maintenance.class, event.getMaintenanceId()));
}
String driverUniqueId = event.getString(Position.KEY_DRIVER_UNIQUE_ID);
if (driverUniqueId != null) {
- velocityContext.put("driver", Context.getDriversManager().getDriverByUniqueId(driverUniqueId));
+ velocityContext.put("driver", cacheManager.findDriverByUniqueId(device.getId(), driverUniqueId));
}
- return velocityContext;
- }
-
- public static NotificationMessage formatMessage(long userId, Event event, Position position, String templatePath) {
- VelocityContext velocityContext = prepareContext(userId, event, position);
- return TextTemplateFormatter.formatMessage(velocityContext, event.getType(), templatePath);
+ return textTemplateFormatter.formatMessage(velocityContext, event.getType(), templatePath);
}
}
diff --git a/src/main/java/org/traccar/notification/NotificatorManager.java b/src/main/java/org/traccar/notification/NotificatorManager.java
index dfd8cd3d6..1d9f4f423 100644
--- a/src/main/java/org/traccar/notification/NotificatorManager.java
+++ b/src/main/java/org/traccar/notification/NotificatorManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,87 +16,71 @@
*/
package org.traccar.notification;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
+import com.google.inject.Injector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.traccar.Context;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
import org.traccar.model.Typed;
+import org.traccar.notificators.Notificator;
import org.traccar.notificators.NotificatorFirebase;
import org.traccar.notificators.NotificatorMail;
import org.traccar.notificators.NotificatorNull;
-import org.traccar.notificators.Notificator;
+import org.traccar.notificators.NotificatorPushover;
import org.traccar.notificators.NotificatorSms;
+import org.traccar.notificators.NotificatorTelegram;
import org.traccar.notificators.NotificatorTraccar;
import org.traccar.notificators.NotificatorWeb;
-import org.traccar.notificators.NotificatorTelegram;
-import org.traccar.notificators.NotificatorPushover;
-public final class NotificatorManager {
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Singleton
+public class NotificatorManager {
private static final Logger LOGGER = LoggerFactory.getLogger(NotificatorManager.class);
- private static final Notificator NULL_NOTIFICATOR = new NotificatorNull();
+ private static final Map<String, Class<? extends Notificator>> NOTIFICATORS_ALL = Map.of(
+ "web", NotificatorWeb.class,
+ "mail", NotificatorMail.class,
+ "sms", NotificatorSms.class,
+ "firebase", NotificatorFirebase.class,
+ "traccar", NotificatorTraccar.class,
+ "telegram", NotificatorTelegram.class,
+ "pushover", NotificatorPushover.class);
- private final Map<String, Notificator> notificators = new HashMap<>();
+ private final Injector injector;
- public NotificatorManager() {
- final String[] types = Context.getConfig().getString("notificator.types", "").split(",");
- for (String type : types) {
- String defaultNotificator = "";
- switch (type) {
- case "web":
- defaultNotificator = NotificatorWeb.class.getCanonicalName();
- break;
- case "mail":
- defaultNotificator = NotificatorMail.class.getCanonicalName();
- break;
- case "sms":
- defaultNotificator = NotificatorSms.class.getCanonicalName();
- break;
- case "firebase":
- defaultNotificator = NotificatorFirebase.class.getCanonicalName();
- break;
- case "traccar":
- defaultNotificator = NotificatorTraccar.class.getCanonicalName();
- break;
- case "telegram":
- defaultNotificator = NotificatorTelegram.class.getCanonicalName();
- break;
- case "pushover":
- defaultNotificator = NotificatorPushover.class.getCanonicalName();
- break;
- default:
- break;
- }
- final String className = Context.getConfig()
- .getString("notificator." + type + ".class", defaultNotificator);
- try {
- notificators.put(type, (Notificator) Class.forName(className).newInstance());
- } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
- LOGGER.warn("Unable to load notificator class for " + type + " " + className + " " + e.getMessage());
- }
+ private final Set<String> types = new HashSet<>();
+
+ @Inject
+ public NotificatorManager(Injector injector, Config config) {
+ this.injector = injector;
+ String types = config.getString(Keys.NOTIFICATOR_TYPES);
+ if (types != null) {
+ this.types.addAll(Arrays.asList(types.split(",")));
}
}
public Notificator getNotificator(String type) {
- final Notificator notificator = notificators.get(type);
- if (notificator == null) {
- LOGGER.warn("No notificator configured for type : " + type);
- return NULL_NOTIFICATOR;
+ var clazz = NOTIFICATORS_ALL.get(type);
+ if (clazz != null) {
+ var notificator = injector.getInstance(clazz);
+ if (notificator != null) {
+ return notificator;
+ }
}
- return notificator;
+ LOGGER.warn("Failed to get notificator {}", type);
+ return new NotificatorNull();
}
public Set<Typed> getAllNotificatorTypes() {
- Set<Typed> result = new HashSet<>();
- for (String notificator : notificators.keySet()) {
- result.add(new Typed(notificator));
- }
- return result;
+ return types.stream().map(Typed::new).collect(Collectors.toUnmodifiableSet());
}
}
diff --git a/src/main/java/org/traccar/notification/PropertiesProvider.java b/src/main/java/org/traccar/notification/PropertiesProvider.java
index f0078feef..91887b5d4 100644
--- a/src/main/java/org/traccar/notification/PropertiesProvider.java
+++ b/src/main/java/org/traccar/notification/PropertiesProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
package org.traccar.notification;
import org.traccar.config.Config;
+import org.traccar.config.ConfigKey;
import org.traccar.model.ExtendedModel;
public class PropertiesProvider {
@@ -32,36 +33,29 @@ public class PropertiesProvider {
this.extendedModel = extendedModel;
}
- public String getString(String key) {
+ public String getString(ConfigKey<String> key) {
if (config != null) {
return config.getString(key);
} else {
- return extendedModel.getString(key);
+ String result = extendedModel.getString(key.getKey());
+ return result != null ? result : key.getDefaultValue();
}
}
- public String getString(String key, String defaultValue) {
- String value = getString(key);
- if (value == null) {
- value = defaultValue;
- }
- return value;
- }
-
- public int getInteger(String key, int defaultValue) {
+ public int getInteger(ConfigKey<Integer> key) {
if (config != null) {
- return config.getInteger(key, defaultValue);
+ return config.getInteger(key);
} else {
- Object result = extendedModel.getAttributes().get(key);
+ Object result = extendedModel.getAttributes().get(key.getKey());
if (result != null) {
return result instanceof String ? Integer.parseInt((String) result) : (Integer) result;
} else {
- return defaultValue;
+ return key.getDefaultValue();
}
}
}
- public Boolean getBoolean(String key) {
+ public Boolean getBoolean(ConfigKey<Boolean> key) {
if (config != null) {
if (config.hasKey(key)) {
return config.getBoolean(key);
@@ -69,7 +63,7 @@ public class PropertiesProvider {
return null;
}
} else {
- Object result = extendedModel.getAttributes().get(key);
+ Object result = extendedModel.getAttributes().get(key.getKey());
if (result != null) {
return result instanceof String ? Boolean.valueOf((String) result) : (Boolean) result;
} else {
diff --git a/src/main/java/org/traccar/notification/TextTemplateFormatter.java b/src/main/java/org/traccar/notification/TextTemplateFormatter.java
index b7058c824..444f4a7c2 100644
--- a/src/main/java/org/traccar/notification/TextTemplateFormatter.java
+++ b/src/main/java/org/traccar/notification/TextTemplateFormatter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,37 +17,56 @@ package org.traccar.notification;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.tools.generic.DateTool;
import org.apache.velocity.tools.generic.NumberTool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.traccar.Context;
+import org.traccar.api.signature.TokenManager;
+import org.traccar.helper.model.UserUtil;
+import org.traccar.model.Server;
import org.traccar.model.User;
-import org.traccar.reports.ReportUtils;
+import org.traccar.storage.StorageException;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
+import java.security.GeneralSecurityException;
import java.util.Locale;
-public final class TextTemplateFormatter {
+@Singleton
+public class TextTemplateFormatter {
private static final Logger LOGGER = LoggerFactory.getLogger(TextTemplateFormatter.class);
- private TextTemplateFormatter() {
+ private final VelocityEngine velocityEngine;
+ private final TokenManager tokenManager;
+
+ @Inject
+ public TextTemplateFormatter(VelocityEngine velocityEngine, TokenManager tokenManager) {
+ this.velocityEngine = velocityEngine;
+ this.tokenManager = tokenManager;
}
- public static VelocityContext prepareContext(User user) {
+ public VelocityContext prepareContext(Server server, User user) {
VelocityContext velocityContext = new VelocityContext();
if (user != null) {
velocityContext.put("user", user);
- velocityContext.put("timezone", ReportUtils.getTimezone(user.getId()));
+ velocityContext.put("timezone", UserUtil.getTimezone(server, user));
+ try {
+ velocityContext.put("token", tokenManager.generateToken(user.getId()));
+ } catch (IOException | GeneralSecurityException | StorageException e) {
+ LOGGER.warn("Token generation failed", e);
+ }
}
- velocityContext.put("webUrl", Context.getVelocityEngine().getProperty("web.url"));
+ velocityContext.put("webUrl", velocityEngine.getProperty("web.url"));
velocityContext.put("dateTool", new DateTool());
velocityContext.put("numberTool", new NumberTool());
velocityContext.put("locale", Locale.getDefault());
@@ -55,23 +74,23 @@ public final class TextTemplateFormatter {
return velocityContext;
}
- public static Template getTemplate(String name, String path) {
+ public Template getTemplate(String name, String path) {
String templateFilePath;
Template template;
try {
templateFilePath = Paths.get(path, name + ".vm").toString();
- template = Context.getVelocityEngine().getTemplate(templateFilePath, StandardCharsets.UTF_8.name());
+ template = velocityEngine.getTemplate(templateFilePath, StandardCharsets.UTF_8.name());
} catch (ResourceNotFoundException error) {
LOGGER.warn("Notification template error", error);
templateFilePath = Paths.get(path, "unknown.vm").toString();
- template = Context.getVelocityEngine().getTemplate(templateFilePath, StandardCharsets.UTF_8.name());
+ template = velocityEngine.getTemplate(templateFilePath, StandardCharsets.UTF_8.name());
}
return template;
}
- public static NotificationMessage formatMessage(VelocityContext velocityContext, String name, String templatePath) {
+ public NotificationMessage formatMessage(VelocityContext velocityContext, String name, String templatePath) {
StringWriter writer = new StringWriter();
getTemplate(name, templatePath).merge(velocityContext, writer);
return new NotificationMessage((String) velocityContext.get("subject"), writer.toString());
diff --git a/src/main/java/org/traccar/notificators/Notificator.java b/src/main/java/org/traccar/notificators/Notificator.java
index dd888bae9..052365c7a 100644
--- a/src/main/java/org/traccar/notificators/Notificator.java
+++ b/src/main/java/org/traccar/notificators/Notificator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,27 +16,13 @@
*/
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;
import org.traccar.notification.MessageException;
-public abstract class Notificator {
+public interface Notificator {
- private static final Logger LOGGER = LoggerFactory.getLogger(Notificator.class);
-
- public void sendAsync(final long userId, final Event event, final Position position) {
- new Thread(() -> {
- try {
- sendSync(userId, event, position);
- } catch (MessageException | InterruptedException error) {
- LOGGER.warn("Event send error", error);
- }
- }).start();
- }
-
- public abstract void sendSync(long userId, Event event, Position position)
- throws MessageException, InterruptedException;
+ void send(User user, Event event, Position position) throws MessageException, InterruptedException;
}
diff --git a/src/main/java/org/traccar/notificators/NotificatorFirebase.java b/src/main/java/org/traccar/notificators/NotificatorFirebase.java
index f91ec25a0..5ce2cbc0b 100644
--- a/src/main/java/org/traccar/notificators/NotificatorFirebase.java
+++ b/src/main/java/org/traccar/notificators/NotificatorFirebase.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,88 +16,91 @@
*/
package org.traccar.notificators;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.traccar.Context;
+import com.google.auth.oauth2.GoogleCredentials;
+import com.google.firebase.FirebaseApp;
+import com.google.firebase.FirebaseOptions;
+import com.google.firebase.messaging.AndroidConfig;
+import com.google.firebase.messaging.AndroidNotification;
+import com.google.firebase.messaging.ApnsConfig;
+import com.google.firebase.messaging.Aps;
+import com.google.firebase.messaging.FirebaseMessaging;
+import com.google.firebase.messaging.FirebaseMessagingException;
+import com.google.firebase.messaging.MulticastMessage;
+import com.google.firebase.messaging.Notification;
+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.NotificationMessage;
+import org.traccar.notification.MessageException;
import org.traccar.notification.NotificationFormatter;
-import javax.ws.rs.client.Entity;
-import javax.ws.rs.client.InvocationCallback;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.List;
-public class NotificatorFirebase extends Notificator {
+@Singleton
+public class NotificatorFirebase implements Notificator {
- private static final Logger LOGGER = LoggerFactory.getLogger(NotificatorFirebase.class);
+ private final NotificationFormatter notificationFormatter;
- private final String url;
- private final String key;
+ @Inject
+ public NotificatorFirebase(Config config, NotificationFormatter notificationFormatter) throws IOException {
- public static class Notification {
- @JsonProperty("title")
- private String title;
- @JsonProperty("body")
- private String body;
- @JsonProperty("sound")
- private String sound;
- }
+ this.notificationFormatter = notificationFormatter;
- public static class Message {
- @JsonProperty("registration_ids")
- private String[] tokens;
- @JsonProperty("notification")
- private Notification notification;
- }
+ InputStream serviceAccount = new ByteArrayInputStream(
+ config.getString(Keys.NOTIFICATOR_FIREBASE_SERVICE_ACCOUNT).getBytes());
- public NotificatorFirebase() {
- this(
- "https://fcm.googleapis.com/fcm/send",
- Context.getConfig().getString(Keys.NOTIFICATOR_FIREBASE_KEY));
- }
+ FirebaseOptions options = FirebaseOptions.builder()
+ .setCredentials(GoogleCredentials.fromStream(serviceAccount))
+ .build();
- protected NotificatorFirebase(String url, String key) {
- this.url = url;
- this.key = key;
+ FirebaseApp.initializeApp(options);
}
@Override
- public void sendSync(long userId, Event event, Position position) {
- final User user = Context.getPermissionsManager().getUser(userId);
- if (user.getAttributes().containsKey("notificationTokens")) {
-
- NotificationMessage shortMessage = NotificationFormatter.formatMessage(userId, event, position, "short");
-
- Notification notification = new Notification();
- notification.title = shortMessage.getSubject();
- notification.body = shortMessage.getBody();
- notification.sound = "default";
-
- Message message = new Message();
- message.tokens = user.getString("notificationTokens").split("[, ]");
- message.notification = notification;
-
- Context.getClient().target(url).request()
- .header("Authorization", "key=" + key)
- .async().post(Entity.json(message), new InvocationCallback<Object>() {
- @Override
- public void completed(Object o) {
- }
-
- @Override
- public void failed(Throwable throwable) {
- LOGGER.warn("Firebase notification error", throwable);
+ public void send(User user, Event event, Position position) throws MessageException {
+ if (user.hasAttribute("notificationTokens")) {
+
+ var shortMessage = notificationFormatter.formatMessage(user, event, position, "short");
+
+ List<String> registrationTokens = Arrays.asList(user.getString("notificationTokens").split("[, ]"));
+
+ MulticastMessage message = MulticastMessage.builder()
+ .setNotification(Notification.builder()
+ .setTitle(shortMessage.getSubject())
+ .setBody(shortMessage.getBody())
+ .build())
+ .setAndroidConfig(AndroidConfig.builder()
+ .setNotification(AndroidNotification.builder()
+ .setSound("default")
+ .build())
+ .build())
+ .setApnsConfig(ApnsConfig.builder()
+ .setAps(Aps.builder()
+ .setSound("default")
+ .build())
+ .build())
+ .addAllTokens(registrationTokens)
+ .putData("eventId", String.valueOf(event.getId()))
+ .build();
+
+ try {
+ var result = FirebaseMessaging.getInstance().sendMulticast(message);
+ for (var response : result.getResponses()) {
+ if (!response.isSuccessful()) {
+ throw new MessageException(response.getException());
+ }
}
- });
+ } catch (FirebaseMessagingException e) {
+ throw new MessageException(e);
+ }
}
}
- @Override
- public void sendAsync(long userId, Event event, Position position) {
- sendSync(userId, event, position);
- }
-
}
diff --git a/src/main/java/org/traccar/notificators/NotificatorMail.java b/src/main/java/org/traccar/notificators/NotificatorMail.java
index 9b5637ed8..19fde6756 100644
--- a/src/main/java/org/traccar/notificators/NotificatorMail.java
+++ b/src/main/java/org/traccar/notificators/NotificatorMail.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2017 - 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,22 +16,34 @@
*/
package org.traccar.notificators;
-import org.traccar.Context;
+import org.traccar.mail.MailManager;
import org.traccar.model.Event;
import org.traccar.model.Position;
-import org.traccar.notification.NotificationMessage;
+import org.traccar.model.User;
import org.traccar.notification.MessageException;
import org.traccar.notification.NotificationFormatter;
+import javax.inject.Inject;
+import javax.inject.Singleton;
import javax.mail.MessagingException;
-public final class NotificatorMail extends Notificator {
+@Singleton
+public class NotificatorMail implements Notificator {
+
+ private final MailManager mailManager;
+ private final NotificationFormatter notificationFormatter;
+
+ @Inject
+ public NotificatorMail(MailManager mailManager, NotificationFormatter notificationFormatter) {
+ this.mailManager = mailManager;
+ this.notificationFormatter = notificationFormatter;
+ }
@Override
- public void sendSync(long userId, Event event, Position position) throws MessageException {
+ public void send(User user, Event event, Position position) throws MessageException {
try {
- NotificationMessage fullMessage = NotificationFormatter.formatMessage(userId, event, position, "full");
- Context.getMailManager().sendMessage(userId, fullMessage.getSubject(), fullMessage.getBody());
+ var fullMessage = notificationFormatter.formatMessage(user, event, position, "full");
+ mailManager.sendMessage(user, fullMessage.getSubject(), fullMessage.getBody());
} catch (MessagingException e) {
throw new MessageException(e);
}
diff --git a/src/main/java/org/traccar/notificators/NotificatorNull.java b/src/main/java/org/traccar/notificators/NotificatorNull.java
index 9364336be..ba9ade9c6 100644
--- a/src/main/java/org/traccar/notificators/NotificatorNull.java
+++ b/src/main/java/org/traccar/notificators/NotificatorNull.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,18 +20,14 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.model.Event;
import org.traccar.model.Position;
+import org.traccar.model.User;
-public final class NotificatorNull extends Notificator {
+public class NotificatorNull implements Notificator {
private static final Logger LOGGER = LoggerFactory.getLogger(NotificatorNull.class);
@Override
- public void sendAsync(long userId, Event event, Position position) {
- LOGGER.warn("You are using null notificatior, please check your configuration, notification not sent");
- }
-
- @Override
- public void sendSync(long userId, Event event, Position position) {
+ 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 456c2fe4f..e00db0579 100644
--- a/src/main/java/org/traccar/notificators/NotificatorPushover.java
+++ b/src/main/java/org/traccar/notificators/NotificatorPushover.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2020 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,22 +16,23 @@
package org.traccar.notificators;
import com.fasterxml.jackson.annotation.JsonProperty;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.traccar.Context;
+import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.model.Event;
import org.traccar.model.Position;
import org.traccar.model.User;
import org.traccar.notification.NotificationFormatter;
-import org.traccar.notification.NotificationMessage;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.ws.rs.client.Client;
import javax.ws.rs.client.Entity;
-import javax.ws.rs.client.InvocationCallback;
-public class NotificatorPushover extends Notificator {
+@Singleton
+public class NotificatorPushover implements Notificator {
- private static final Logger LOGGER = LoggerFactory.getLogger(NotificatorPushover.class);
+ private final NotificationFormatter notificationFormatter;
+ private final Client client;
private final String url;
private final String token;
@@ -50,58 +51,35 @@ public class NotificatorPushover extends Notificator {
private String message;
}
- public NotificatorPushover() {
+ @Inject
+ public NotificatorPushover(Config config, NotificationFormatter notificationFormatter, Client client) {
+ this.notificationFormatter = notificationFormatter;
+ this.client = client;
url = "https://api.pushover.net/1/messages.json";
- token = Context.getConfig().getString(Keys.NOTIFICATOR_PUSHOVER_TOKEN);
- user = Context.getConfig().getString(Keys.NOTIFICATOR_PUSHOVER_USER);
+ token = config.getString(Keys.NOTIFICATOR_PUSHOVER_TOKEN);
+ user = config.getString(Keys.NOTIFICATOR_PUSHOVER_USER);
}
@Override
- public void sendSync(long userId, Event event, Position position) {
+ public void send(User user, Event event, Position position) {
+ var shortMessage = notificationFormatter.formatMessage(user, event, position, "short");
- final User user = Context.getPermissionsManager().getUser(userId);
-
- String device = "";
-
- if (user.getAttributes().containsKey("notificator.pushover.device")) {
- device = user.getString("notificator.pushover.device").replaceAll(" *, *", ",");
- }
+ Message message = new Message();
+ message.token = token;
- if (token == null) {
- LOGGER.warn("Pushover token not found");
- return;
+ message.user = user.getString("pushoverUserKey");
+ if (message.user == null) {
+ message.user = this.user;
}
- if (this.user == null) {
- LOGGER.warn("Pushover user not found");
- return;
+ if (user.hasAttribute("pushoverDeviceNames")) {
+ message.device = user.getString("pushoverDeviceNames").replaceAll(" *, *", ",");
}
- NotificationMessage shortMessage = NotificationFormatter.formatMessage(userId, event, position, "short");
-
- Message message = new Message();
- message.token = token;
- message.user = this.user;
- message.device = device;
message.title = shortMessage.getSubject();
message.message = shortMessage.getBody();
- Context.getClient().target(url).request()
- .async().post(Entity.json(message), new InvocationCallback<Object>() {
- @Override
- public void completed(Object o) {
- }
-
- @Override
- public void failed(Throwable throwable) {
- LOGGER.warn("Pushover API error", throwable);
- }
- });
- }
-
- @Override
- public void sendAsync(long userId, Event event, Position position) {
- sendSync(userId, event, position);
+ client.target(url).request().post(Entity.json(message)).close();
}
}
diff --git a/src/main/java/org/traccar/notificators/NotificatorSms.java b/src/main/java/org/traccar/notificators/NotificatorSms.java
index fb817b112..e37d10888 100644
--- a/src/main/java/org/traccar/notificators/NotificatorSms.java
+++ b/src/main/java/org/traccar/notificators/NotificatorSms.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2017 - 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,37 +16,38 @@
*/
package org.traccar.notificators;
-import org.traccar.Context;
-import org.traccar.Main;
import org.traccar.database.StatisticsManager;
import org.traccar.model.Event;
import org.traccar.model.Position;
import org.traccar.model.User;
import org.traccar.notification.MessageException;
import org.traccar.notification.NotificationFormatter;
-import org.traccar.notification.NotificationMessage;
+import org.traccar.sms.SmsManager;
-public final class NotificatorSms extends Notificator {
+import javax.inject.Inject;
+import javax.inject.Singleton;
- @Override
- public void sendAsync(long userId, Event event, Position position) {
- final User user = Context.getPermissionsManager().getUser(userId);
- if (user.getPhone() != null) {
- NotificationMessage shortMessage = NotificationFormatter.formatMessage(userId, event, position, "short");
- Main.getInjector().getInstance(StatisticsManager.class).registerSms();
- Context.getSmsManager().sendMessageAsync(user.getPhone(),
- shortMessage.getBody(), false);
- }
+@Singleton
+public class NotificatorSms implements Notificator {
+
+ private final SmsManager smsManager;
+ private final NotificationFormatter notificationFormatter;
+ private final StatisticsManager statisticsManager;
+
+ @Inject
+ public NotificatorSms(
+ SmsManager smsManager, NotificationFormatter notificationFormatter, StatisticsManager statisticsManager) {
+ this.smsManager = smsManager;
+ this.notificationFormatter = notificationFormatter;
+ this.statisticsManager = statisticsManager;
}
@Override
- public void sendSync(long userId, Event event, Position position) throws MessageException, InterruptedException {
- final User user = Context.getPermissionsManager().getUser(userId);
+ public void send(User user, Event event, Position position) throws MessageException, InterruptedException {
if (user.getPhone() != null) {
- NotificationMessage shortMessage = NotificationFormatter.formatMessage(userId, event, position, "short");
- Main.getInjector().getInstance(StatisticsManager.class).registerSms();
- Context.getSmsManager().sendMessageSync(user.getPhone(),
- shortMessage.getBody(), false);
+ var shortMessage = notificationFormatter.formatMessage(user, event, position, "short");
+ statisticsManager.registerSms();
+ smsManager.sendMessage(user.getPhone(), shortMessage.getBody(), false);
}
}
diff --git a/src/main/java/org/traccar/notificators/NotificatorTelegram.java b/src/main/java/org/traccar/notificators/NotificatorTelegram.java
index 70148110c..38e87c222 100644
--- a/src/main/java/org/traccar/notificators/NotificatorTelegram.java
+++ b/src/main/java/org/traccar/notificators/NotificatorTelegram.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2022 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,23 @@
package org.traccar.notificators;
import com.fasterxml.jackson.annotation.JsonProperty;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.traccar.Context;
-import org.traccar.model.User;
+import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.model.Event;
import org.traccar.model.Position;
+import org.traccar.model.User;
import org.traccar.notification.NotificationFormatter;
-import org.traccar.notification.NotificationMessage;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.ws.rs.client.Client;
import javax.ws.rs.client.Entity;
-import javax.ws.rs.client.InvocationCallback;
-public class NotificatorTelegram extends Notificator {
+@Singleton
+public class NotificatorTelegram implements Notificator {
- private static final Logger LOGGER = LoggerFactory.getLogger(NotificatorTelegram.class);
+ private final NotificationFormatter notificationFormatter;
+ private final Client client;
private final String urlSendText;
private final String urlSendLocation;
@@ -61,29 +62,16 @@ public class NotificatorTelegram extends Notificator {
private int bearing;
}
- public NotificatorTelegram() {
+ @Inject
+ public NotificatorTelegram(Config config, NotificationFormatter notificationFormatter, Client client) {
+ this.notificationFormatter = notificationFormatter;
+ this.client = client;
urlSendText = String.format(
- "https://api.telegram.org/bot%s/sendMessage",
- Context.getConfig().getString(Keys.NOTIFICATOR_TELEGRAM_KEY));
+ "https://api.telegram.org/bot%s/sendMessage", config.getString(Keys.NOTIFICATOR_TELEGRAM_KEY));
urlSendLocation = String.format(
- "https://api.telegram.org/bot%s/sendLocation",
- Context.getConfig().getString(Keys.NOTIFICATOR_TELEGRAM_KEY));
- chatId = Context.getConfig().getString(Keys.NOTIFICATOR_TELEGRAM_CHAT_ID);
- sendLocation = Context.getConfig().getBoolean(Keys.NOTIFICATOR_TELEGRAM_SEND_LOCATION);
- }
-
- private void executeRequest(String url, Object message) {
- Context.getClient().target(url).request()
- .async().post(Entity.json(message), new InvocationCallback<Object>() {
- @Override
- public void completed(Object o) {
- }
-
- @Override
- public void failed(Throwable throwable) {
- LOGGER.warn("Telegram API error", throwable);
- }
- });
+ "https://api.telegram.org/bot%s/sendLocation", config.getString(Keys.NOTIFICATOR_TELEGRAM_KEY));
+ chatId = config.getString(Keys.NOTIFICATOR_TELEGRAM_CHAT_ID);
+ sendLocation = config.getBoolean(Keys.NOTIFICATOR_TELEGRAM_SEND_LOCATION);
}
private LocationMessage createLocationMessage(String messageChatId, Position position) {
@@ -97,9 +85,8 @@ public class NotificatorTelegram extends Notificator {
}
@Override
- public void sendSync(long userId, Event event, Position position) {
- User user = Context.getPermissionsManager().getUser(userId);
- NotificationMessage shortMessage = NotificationFormatter.formatMessage(userId, event, position, "short");
+ public void send(User user, Event event, Position position) {
+ var shortMessage = notificationFormatter.formatMessage(user, event, position, "short");
TextMessage message = new TextMessage();
message.chatId = user.getString("telegramChatId");
@@ -107,15 +94,11 @@ public class NotificatorTelegram extends Notificator {
message.chatId = chatId;
}
message.text = shortMessage.getBody();
- executeRequest(urlSendText, message);
+ client.target(urlSendText).request().post(Entity.json(message)).close();
if (sendLocation && position != null) {
- executeRequest(urlSendLocation, createLocationMessage(message.chatId, position));
+ client.target(urlSendLocation).request().post(
+ Entity.json(createLocationMessage(message.chatId, position))).close();
}
}
- @Override
- public void sendAsync(long userId, Event event, Position position) {
- sendSync(userId, event, position);
- }
-
}
diff --git a/src/main/java/org/traccar/notificators/NotificatorTraccar.java b/src/main/java/org/traccar/notificators/NotificatorTraccar.java
index 5bcd18b5e..9ae39f975 100644
--- a/src/main/java/org/traccar/notificators/NotificatorTraccar.java
+++ b/src/main/java/org/traccar/notificators/NotificatorTraccar.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2020 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,15 +15,69 @@
*/
package org.traccar.notificators;
-import org.traccar.Context;
+import com.fasterxml.jackson.annotation.JsonProperty;
+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;
-public class NotificatorTraccar extends NotificatorFirebase {
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.Entity;
- public NotificatorTraccar() {
- super(
- "https://www.traccar.org/push/",
- Context.getConfig().getString(Keys.NOTIFICATOR_TRACCAR_KEY));
+@Singleton
+public class NotificatorTraccar implements Notificator {
+
+ private final NotificationFormatter notificationFormatter;
+ private final Client client;
+
+ private final String url;
+ private final String key;
+
+ public static class Notification {
+ @JsonProperty("title")
+ private String title;
+ @JsonProperty("body")
+ private String body;
+ @JsonProperty("sound")
+ private String sound;
+ }
+
+ public static class Message {
+ @JsonProperty("registration_ids")
+ private String[] tokens;
+ @JsonProperty("notification")
+ private Notification notification;
+ }
+
+ @Inject
+ public NotificatorTraccar(Config config, NotificationFormatter notificationFormatter, Client client) {
+ this.notificationFormatter = notificationFormatter;
+ this.client = client;
+ 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) {
+ if (user.hasAttribute("notificationTokens")) {
+
+ var shortMessage = notificationFormatter.formatMessage(user, event, position, "short");
+
+ Notification notification = new Notification();
+ notification.title = shortMessage.getSubject();
+ notification.body = shortMessage.getBody();
+ notification.sound = "default";
+
+ Message message = new Message();
+ message.tokens = user.getString("notificationTokens").split("[, ]");
+ message.notification = notification;
+
+ client.target(url).request().header("Authorization", "key=" + key).post(Entity.json(message)).close();
+ }
}
}
diff --git a/src/main/java/org/traccar/notificators/NotificatorWeb.java b/src/main/java/org/traccar/notificators/NotificatorWeb.java
index 1d11c0b46..deabeade1 100644
--- a/src/main/java/org/traccar/notificators/NotificatorWeb.java
+++ b/src/main/java/org/traccar/notificators/NotificatorWeb.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,15 +16,45 @@
*/
package org.traccar.notificators;
-import org.traccar.Context;
import org.traccar.model.Event;
import org.traccar.model.Position;
+import org.traccar.model.User;
+import org.traccar.notification.NotificationFormatter;
+import org.traccar.session.ConnectionManager;
-public final class NotificatorWeb extends Notificator {
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton
+public final class NotificatorWeb implements Notificator {
+
+ private final ConnectionManager connectionManager;
+ private final NotificationFormatter notificationFormatter;
+
+ @Inject
+ public NotificatorWeb(
+ ConnectionManager connectionManager, NotificationFormatter notificationFormatter) {
+ this.connectionManager = connectionManager;
+ this.notificationFormatter = notificationFormatter;
+ }
@Override
- public void sendSync(long userId, Event event, Position position) {
- Context.getConnectionManager().updateEvent(userId, event);
+ public void send(User user, Event event, Position position) {
+
+ Event copy = new Event();
+ copy.setId(event.getId());
+ copy.setDeviceId(event.getDeviceId());
+ copy.setType(event.getType());
+ copy.setEventTime(event.getEventTime());
+ copy.setPositionId(event.getPositionId());
+ copy.setGeofenceId(event.getGeofenceId());
+ copy.setMaintenanceId(event.getMaintenanceId());
+ copy.getAttributes().putAll(event.getAttributes());
+
+ var message = notificationFormatter.formatMessage(user, event, position, "short");
+ copy.set("message", message.getBody());
+
+ connectionManager.updateEvent(true, user.getId(), copy);
}
}
diff --git a/src/main/java/org/traccar/protocol/AdmProtocol.java b/src/main/java/org/traccar/protocol/AdmProtocol.java
index d1d81118c..bab1d2339 100644
--- a/src/main/java/org/traccar/protocol/AdmProtocol.java
+++ b/src/main/java/org/traccar/protocol/AdmProtocol.java
@@ -19,17 +19,21 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class AdmProtocol extends BaseProtocol {
- public AdmProtocol() {
+ @Inject
+ public AdmProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_GET_DEVICE_STATUS,
Command.TYPE_CUSTOM);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new AdmFrameDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new AdmProtocolEncoder(AdmProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/AdmProtocolDecoder.java b/src/main/java/org/traccar/protocol/AdmProtocolDecoder.java
index 7e3478704..1f940f7e2 100644
--- a/src/main/java/org/traccar/protocol/AdmProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AdmProtocolDecoder.java
@@ -18,7 +18,7 @@ package org.traccar.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.UnitsConverter;
diff --git a/src/main/java/org/traccar/protocol/AisProtocol.java b/src/main/java/org/traccar/protocol/AisProtocol.java
index 3b9cad7c8..bc975c277 100644
--- a/src/main/java/org/traccar/protocol/AisProtocol.java
+++ b/src/main/java/org/traccar/protocol/AisProtocol.java
@@ -19,13 +19,17 @@ import io.netty.handler.codec.string.StringDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class AisProtocol extends BaseProtocol {
- public AisProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public AisProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringDecoder());
pipeline.addLast(new AisProtocolDecoder(AisProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/AisProtocolDecoder.java b/src/main/java/org/traccar/protocol/AisProtocolDecoder.java
index 8970f3d4a..a434e6e33 100644
--- a/src/main/java/org/traccar/protocol/AisProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AisProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BitBuffer;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/AlematicsProtocol.java b/src/main/java/org/traccar/protocol/AlematicsProtocol.java
index 8da2356b9..b85b44382 100644
--- a/src/main/java/org/traccar/protocol/AlematicsProtocol.java
+++ b/src/main/java/org/traccar/protocol/AlematicsProtocol.java
@@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class AlematicsProtocol extends BaseProtocol {
- public AlematicsProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public AlematicsProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new AlematicsFrameDecoder(1024));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/AlematicsProtocolDecoder.java b/src/main/java/org/traccar/protocol/AlematicsProtocolDecoder.java
index 25ccf6856..981437191 100644
--- a/src/main/java/org/traccar/protocol/AlematicsProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AlematicsProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/AnytrekProtocol.java b/src/main/java/org/traccar/protocol/AnytrekProtocol.java
index 9bd0c9163..b0e974c69 100644
--- a/src/main/java/org/traccar/protocol/AnytrekProtocol.java
+++ b/src/main/java/org/traccar/protocol/AnytrekProtocol.java
@@ -19,15 +19,19 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import java.nio.ByteOrder;
+import javax.inject.Inject;
+
public class AnytrekProtocol extends BaseProtocol {
- public AnytrekProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public AnytrekProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 2, 2, 2, 0, true));
pipeline.addLast(new AnytrekProtocolDecoder(AnytrekProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/AnytrekProtocolDecoder.java b/src/main/java/org/traccar/protocol/AnytrekProtocolDecoder.java
index c48f59c90..0f9c2b17a 100644
--- a/src/main/java/org/traccar/protocol/AnytrekProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AnytrekProtocolDecoder.java
@@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/ApelProtocol.java b/src/main/java/org/traccar/protocol/ApelProtocol.java
index 382aa16af..f1d6e659c 100644
--- a/src/main/java/org/traccar/protocol/ApelProtocol.java
+++ b/src/main/java/org/traccar/protocol/ApelProtocol.java
@@ -19,14 +19,18 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import java.nio.ByteOrder;
+import javax.inject.Inject;
+
public class ApelProtocol extends BaseProtocol {
- public ApelProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public ApelProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 2, 2, 4, 0, true));
pipeline.addLast(new ApelProtocolDecoder(ApelProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/ApelProtocolDecoder.java b/src/main/java/org/traccar/protocol/ApelProtocolDecoder.java
index c95a0366a..97ed7de96 100644
--- a/src/main/java/org/traccar/protocol/ApelProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ApelProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Checksum;
diff --git a/src/main/java/org/traccar/protocol/AplicomProtocol.java b/src/main/java/org/traccar/protocol/AplicomProtocol.java
index 2b9dbf97c..47bb780cb 100644
--- a/src/main/java/org/traccar/protocol/AplicomProtocol.java
+++ b/src/main/java/org/traccar/protocol/AplicomProtocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class AplicomProtocol extends BaseProtocol {
- public AplicomProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public AplicomProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new AplicomFrameDecoder());
pipeline.addLast(new AplicomProtocolDecoder(AplicomProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/AplicomProtocolDecoder.java b/src/main/java/org/traccar/protocol/AplicomProtocolDecoder.java
index f11312428..0cd8ca37e 100644
--- a/src/main/java/org/traccar/protocol/AplicomProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AplicomProtocolDecoder.java
@@ -21,8 +21,7 @@ import io.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.config.Keys;
import org.traccar.helper.Checksum;
@@ -303,7 +302,7 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
decodeEventData(position, buf, event);
}
- if (Context.getConfig().getBoolean(Keys.PROTOCOL_CAN.withPrefix(getProtocolName()))
+ if (getConfig().getBoolean(Keys.PROTOCOL_CAN.withPrefix(getProtocolName()))
&& buf.isReadable() && (selector & 0x1000) != 0 && event == EVENT_DATA) {
decodeCanData(buf, position);
}
diff --git a/src/main/java/org/traccar/protocol/AppelloProtocol.java b/src/main/java/org/traccar/protocol/AppelloProtocol.java
index 1ca4168e4..25b2bf3b8 100644
--- a/src/main/java/org/traccar/protocol/AppelloProtocol.java
+++ b/src/main/java/org/traccar/protocol/AppelloProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class AppelloProtocol extends BaseProtocol {
- public AppelloProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public AppelloProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/AppelloProtocolDecoder.java b/src/main/java/org/traccar/protocol/AppelloProtocolDecoder.java
index 47e329234..8e182b9fb 100644
--- a/src/main/java/org/traccar/protocol/AppelloProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AppelloProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/AquilaProtocol.java b/src/main/java/org/traccar/protocol/AquilaProtocol.java
index 5ca1ec091..6080df33d 100644
--- a/src/main/java/org/traccar/protocol/AquilaProtocol.java
+++ b/src/main/java/org/traccar/protocol/AquilaProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class AquilaProtocol extends BaseProtocol {
- public AquilaProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public AquilaProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/AquilaProtocolDecoder.java b/src/main/java/org/traccar/protocol/AquilaProtocolDecoder.java
index 3c43ddf2a..50ff10469 100644
--- a/src/main/java/org/traccar/protocol/AquilaProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AquilaProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/Ardi01Protocol.java b/src/main/java/org/traccar/protocol/Ardi01Protocol.java
index f7826430f..b33c2817f 100644
--- a/src/main/java/org/traccar/protocol/Ardi01Protocol.java
+++ b/src/main/java/org/traccar/protocol/Ardi01Protocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class Ardi01Protocol extends BaseProtocol {
- public Ardi01Protocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public Ardi01Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/Ardi01ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Ardi01ProtocolDecoder.java
index 85e9ecfde..07653623a 100644
--- a/src/main/java/org/traccar/protocol/Ardi01ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Ardi01ProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/ArknavProtocol.java b/src/main/java/org/traccar/protocol/ArknavProtocol.java
index 3b485e4a5..4f443aa3a 100644
--- a/src/main/java/org/traccar/protocol/ArknavProtocol.java
+++ b/src/main/java/org/traccar/protocol/ArknavProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,19 +21,32 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class ArknavProtocol extends BaseProtocol {
- public ArknavProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public ArknavProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '\r'));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new ArknavProtocolDecoder(ArknavProtocol.this));
}
});
+ addServer(new TrackerServer(config, getName(), true) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new ArknavProtocolDecoder(ArknavProtocol.this));
+ }
+ });
+
}
}
diff --git a/src/main/java/org/traccar/protocol/ArknavProtocolDecoder.java b/src/main/java/org/traccar/protocol/ArknavProtocolDecoder.java
index 4982e02fc..4def9c979 100644
--- a/src/main/java/org/traccar/protocol/ArknavProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ArknavProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/ArknavX8Protocol.java b/src/main/java/org/traccar/protocol/ArknavX8Protocol.java
index a29bc1ad3..39c6e8009 100644
--- a/src/main/java/org/traccar/protocol/ArknavX8Protocol.java
+++ b/src/main/java/org/traccar/protocol/ArknavX8Protocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class ArknavX8Protocol extends BaseProtocol {
- public ArknavX8Protocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public ArknavX8Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ';'));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/ArknavX8ProtocolDecoder.java b/src/main/java/org/traccar/protocol/ArknavX8ProtocolDecoder.java
index b570f5423..22c0344d6 100644
--- a/src/main/java/org/traccar/protocol/ArknavX8ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ArknavX8ProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/ArmoliProtocol.java b/src/main/java/org/traccar/protocol/ArmoliProtocol.java
new file mode 100644
index 000000000..32fba3b52
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ArmoliProtocol.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.CharacterDelimiterFrameDecoder;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
+
+public class ArmoliProtocol extends BaseProtocol {
+
+ @Inject
+ public ArmoliProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ";;", ";\r", ";"));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new ArmoliProtocolDecoder(ArmoliProtocol.this));
+ pipeline.addLast(new ArmoliProtocolPoller(ArmoliProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/ArmoliProtocolDecoder.java b/src/main/java/org/traccar/protocol/ArmoliProtocolDecoder.java
new file mode 100644
index 000000000..cbed64f76
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ArmoliProtocolDecoder.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.session.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.ObdDecoder;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.regex.Pattern;
+
+public class ArmoliProtocolDecoder extends BaseProtocolDecoder {
+
+ public ArmoliProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("[M") // start
+ .number("(d{15})") // imei
+ .number("(dd)(dd)(dd)") // date (ddmmyy)
+ .number("(dd)(dd)(dd)") // time (hhmmss)
+ .number("([NS])(dd.d{6})") // latitude
+ .number("([EW])(ddd.d{6})") // longitude
+ .number("(d)") // valid
+ .number("(x)") // satellites
+ .number("(xx)") // speed
+ .number("(ddd)") // course
+ .number("(xxx)") // adc 1
+ .number("(xxx)") // adc 2
+ .number("(xx)") // status
+ .number("(xx)") // max speed
+ .number("(x{6})") // distance
+ .number("(dd)?") // hdop
+ .number("x{4}") // idle
+ .number(":(x+)").optional() // alarms
+ .number("G(x{6})").optional() // g-sensor
+ .number("H(x{3})").optional() // power
+ .number("E(x{3})").optional() // battery
+ .number("!(x+)").optional() // driver
+ .expression("@A([>0-9A-F]+)").optional() // obd
+ .any()
+ .compile();
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = (String) msg;
+ char type = sentence.charAt(1);
+
+ Position position = new Position(getProtocolName());
+ DeviceSession deviceSession;
+
+ if (type != 'M') {
+ if (type == 'W') {
+ deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession != null) {
+ position.setDeviceId(deviceSession.getDeviceId());
+ getLastLocation(position, null);
+ position.set(
+ Position.KEY_RESULT,
+ sentence.substring(sentence.indexOf(',') + 1, sentence.length() - 1));
+ return position;
+ }
+ } else if (channel != null && (type == 'Q' || type == 'L')) {
+ channel.writeAndFlush(new NetworkMessage("[TX,];;", remoteAddress));
+ }
+ return null;
+ }
+
+ Parser parser = new Parser(PATTERN, (String) msg);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+ position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG));
+ position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG));
+ position.setValid(parser.nextInt() > 0);
+
+ position.set(Position.KEY_SATELLITES, parser.nextHexInt());
+
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextHexInt()));
+ position.setCourse(parser.nextInt());
+
+ position.set(Position.PREFIX_ADC + 1, parser.nextHexInt() / 27.0 * 1000);
+ position.set(Position.PREFIX_ADC + 1, parser.nextHexInt() / 27.0 * 1000);
+ position.set(Position.KEY_STATUS, parser.nextHexInt());
+ position.set("maxSpeed", parser.nextHexInt());
+ position.set(Position.KEY_ODOMETER, parser.nextHexInt());
+
+ if (parser.hasNext()) {
+ position.set(Position.KEY_HDOP, parser.nextInt() * 0.1);
+ }
+ if (parser.hasNext()) {
+ position.set("alarms", parser.next());
+ }
+ if (parser.hasNext()) {
+ position.set(Position.KEY_G_SENSOR, parser.next());
+ }
+ if (parser.hasNext()) {
+ position.set(Position.KEY_POWER, parser.nextHexInt() * 0.01);
+ }
+ if (parser.hasNext()) {
+ position.set(Position.KEY_BATTERY, parser.nextHexInt() * 0.01);
+ }
+ if (parser.hasNext()) {
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
+ }
+ if (parser.hasNext()) {
+ String[] values = parser.next().split(">");
+ for (int i = 1; i < values.length; i++) {
+ String value = values[i];
+ position.add(ObdDecoder.decodeData(
+ Integer.parseInt(value.substring(4, 6), 16),
+ Long.parseLong(value.substring(6), 16), true));
+ }
+ }
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/DeviceSession.java b/src/main/java/org/traccar/protocol/ArmoliProtocolPoller.java
index 322381807..f7bb9f593 100644
--- a/src/main/java/org/traccar/DeviceSession.java
+++ b/src/main/java/org/traccar/protocol/ArmoliProtocolPoller.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,30 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar;
+package org.traccar.protocol;
-import java.util.TimeZone;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolPoller;
+import org.traccar.Protocol;
-public class DeviceSession {
+import java.net.SocketAddress;
- private final long deviceId;
+public class ArmoliProtocolPoller extends BaseProtocolPoller {
- public DeviceSession(long deviceId) {
- this.deviceId = deviceId;
+ public ArmoliProtocolPoller(Protocol protocol) {
+ super(180000);
}
- public long getDeviceId() {
- return deviceId;
- }
-
- private TimeZone timeZone;
-
- public void setTimeZone(TimeZone timeZone) {
- this.timeZone = timeZone;
- }
-
- public TimeZone getTimeZone() {
- return timeZone;
+ @Override
+ protected void sendRequest(Channel channel, SocketAddress remoteAddress) {
+ channel.writeAndFlush("[TX,];;");
}
}
diff --git a/src/main/java/org/traccar/protocol/ArnaviBinaryProtocolDecoder.java b/src/main/java/org/traccar/protocol/ArnaviBinaryProtocolDecoder.java
index e957a6911..0f6b7a33f 100644
--- a/src/main/java/org/traccar/protocol/ArnaviBinaryProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ArnaviBinaryProtocolDecoder.java
@@ -20,7 +20,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Checksum;
diff --git a/src/main/java/org/traccar/protocol/ArnaviProtocol.java b/src/main/java/org/traccar/protocol/ArnaviProtocol.java
index aecb42c8c..091d5c06f 100644
--- a/src/main/java/org/traccar/protocol/ArnaviProtocol.java
+++ b/src/main/java/org/traccar/protocol/ArnaviProtocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class ArnaviProtocol extends BaseProtocol {
- public ArnaviProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public ArnaviProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new ArnaviFrameDecoder());
pipeline.addLast(new ArnaviProtocolDecoder(ArnaviProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java b/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java
index 68a70c944..361eeeef2 100644
--- a/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2020 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,11 +15,13 @@
*/
package org.traccar.protocol;
+import com.google.inject.Injector;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.Protocol;
+import javax.inject.Inject;
import java.net.SocketAddress;
public class ArnaviProtocolDecoder extends BaseProtocolDecoder {
@@ -33,6 +35,12 @@ public class ArnaviProtocolDecoder extends BaseProtocolDecoder {
binaryProtocolDecoder = new ArnaviBinaryProtocolDecoder(protocol);
}
+ @Inject
+ public void setInjector(Injector injector) {
+ injector.injectMembers(textProtocolDecoder);
+ injector.injectMembers(binaryProtocolDecoder);
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
diff --git a/src/main/java/org/traccar/protocol/ArnaviTextProtocolDecoder.java b/src/main/java/org/traccar/protocol/ArnaviTextProtocolDecoder.java
index b99869e6e..9d82c9ad5 100644
--- a/src/main/java/org/traccar/protocol/ArnaviTextProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ArnaviTextProtocolDecoder.java
@@ -18,7 +18,7 @@ package org.traccar.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/AstraProtocol.java b/src/main/java/org/traccar/protocol/AstraProtocol.java
index 12b0dfb68..021a81e07 100644
--- a/src/main/java/org/traccar/protocol/AstraProtocol.java
+++ b/src/main/java/org/traccar/protocol/AstraProtocol.java
@@ -19,20 +19,24 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class AstraProtocol extends BaseProtocol {
- public AstraProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public AstraProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 1, 2, -3, 0));
pipeline.addLast(new AstraProtocolDecoder(AstraProtocol.this));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new AstraProtocolDecoder(AstraProtocol.this));
}
});
diff --git a/src/main/java/org/traccar/protocol/AstraProtocolDecoder.java b/src/main/java/org/traccar/protocol/AstraProtocolDecoder.java
index e6f546b9f..366bf9e8b 100644
--- a/src/main/java/org/traccar/protocol/AstraProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AstraProtocolDecoder.java
@@ -21,7 +21,7 @@ import io.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/At2000Protocol.java b/src/main/java/org/traccar/protocol/At2000Protocol.java
index 5894f3eab..25e9be86f 100644
--- a/src/main/java/org/traccar/protocol/At2000Protocol.java
+++ b/src/main/java/org/traccar/protocol/At2000Protocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class At2000Protocol extends BaseProtocol {
- public At2000Protocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public At2000Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new At2000FrameDecoder());
pipeline.addLast(new At2000ProtocolDecoder(At2000Protocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/At2000ProtocolDecoder.java b/src/main/java/org/traccar/protocol/At2000ProtocolDecoder.java
index 43798eb67..b81ba306d 100644
--- a/src/main/java/org/traccar/protocol/At2000ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/At2000ProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.DataConverter;
diff --git a/src/main/java/org/traccar/protocol/AtrackProtocol.java b/src/main/java/org/traccar/protocol/AtrackProtocol.java
index 429708b26..21eb09696 100644
--- a/src/main/java/org/traccar/protocol/AtrackProtocol.java
+++ b/src/main/java/org/traccar/protocol/AtrackProtocol.java
@@ -18,24 +18,28 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class AtrackProtocol extends BaseProtocol {
- public AtrackProtocol() {
+ @Inject
+ public AtrackProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_CUSTOM);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new AtrackFrameDecoder());
pipeline.addLast(new AtrackProtocolEncoder(AtrackProtocol.this));
pipeline.addLast(new AtrackProtocolDecoder(AtrackProtocol.this));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new AtrackProtocolEncoder(AtrackProtocol.this));
pipeline.addLast(new AtrackProtocolDecoder(AtrackProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java
index 186b81470..340641729 100644
--- a/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java
@@ -20,8 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.config.Keys;
@@ -54,7 +53,7 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder {
private static final int MIN_DATA_LENGTH = 40;
private boolean longDate;
- private final boolean decimalFuel;
+ private boolean decimalFuel;
private boolean custom;
private String form;
@@ -64,17 +63,20 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder {
public AtrackProtocolDecoder(Protocol protocol) {
super(protocol);
+ }
- longDate = Context.getConfig().getBoolean(Keys.PROTOCOL_LONG_DATE.withPrefix(getProtocolName()));
- decimalFuel = Context.getConfig().getBoolean(Keys.PROTOCOL_DECIMAL_FUEL.withPrefix(getProtocolName()));
+ @Override
+ protected void init() {
+ longDate = getConfig().getBoolean(Keys.PROTOCOL_LONG_DATE.withPrefix(getProtocolName()));
+ decimalFuel = getConfig().getBoolean(Keys.PROTOCOL_DECIMAL_FUEL.withPrefix(getProtocolName()));
- custom = Context.getConfig().getBoolean(Keys.PROTOCOL_CUSTOM.withPrefix(getProtocolName()));
- form = Context.getConfig().getString(Keys.PROTOCOL_FORM.withPrefix(getProtocolName()));
+ custom = getConfig().getBoolean(Keys.PROTOCOL_CUSTOM.withPrefix(getProtocolName()));
+ form = getConfig().getString(Keys.PROTOCOL_FORM.withPrefix(getProtocolName()));
if (form != null) {
custom = true;
}
- String alarmMapString = Context.getConfig().getString(Keys.PROTOCOL_ALARM_MAP.withPrefix(getProtocolName()));
+ String alarmMapString = getConfig().getString(Keys.PROTOCOL_ALARM_MAP.withPrefix(getProtocolName()));
if (alarmMapString != null) {
for (String pair : alarmMapString.split(",")) {
if (!pair.isEmpty()) {
@@ -98,7 +100,7 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder {
this.form = form;
}
- private static void sendResponse(Channel channel, SocketAddress remoteAddress, long rawId, int index) {
+ private void sendResponse(Channel channel, SocketAddress remoteAddress, long rawId, int index) {
if (channel != null) {
ByteBuf response = Unpooled.buffer(12);
response.writeShort(0xfe02);
@@ -524,20 +526,24 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder {
private List<Position> decodeText(Channel channel, SocketAddress remoteAddress, String sentence) {
- int startIndex = -1;
- for (int i = 0; i < 4; i++) {
- startIndex = sentence.indexOf(',', startIndex + 1);
+ int positionIndex = -1;
+ for (int i = 0; i < 5; i++) {
+ positionIndex = sentence.indexOf(',', positionIndex + 1);
}
- int endIndex = sentence.indexOf(',', startIndex + 1);
- String imei = sentence.substring(startIndex + 1, endIndex);
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
+ String[] headers = sentence.substring(0, positionIndex).split(",");
+ long id = Long.parseLong(headers[2]);
+ int index = Integer.parseInt(headers[3]);
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, headers[4]);
if (deviceSession == null) {
return null;
}
+ sendResponse(channel, remoteAddress, id, index);
+
List<Position> positions = new LinkedList<>();
- String[] lines = sentence.substring(endIndex + 1).split("\r\n");
+ String[] lines = sentence.substring(positionIndex + 1).split("\r\n");
for (String line : lines) {
Position position = decodeTextLine(deviceSession, line);
@@ -626,7 +632,7 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder {
getLastLocation(position, new Date(time * 1000));
- position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(String.valueOf(id), photo, "jpg"));
+ position.set(Position.KEY_IMAGE, writeMediaFile(String.valueOf(id), photo, "jpg"));
photo.release();
photo = null;
diff --git a/src/main/java/org/traccar/protocol/AuroProtocol.java b/src/main/java/org/traccar/protocol/AuroProtocol.java
index b8ebdaa75..d37884c8b 100644
--- a/src/main/java/org/traccar/protocol/AuroProtocol.java
+++ b/src/main/java/org/traccar/protocol/AuroProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class AuroProtocol extends BaseProtocol {
- public AuroProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public AuroProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/AuroProtocolDecoder.java b/src/main/java/org/traccar/protocol/AuroProtocolDecoder.java
index d7916147b..4489cf27e 100644
--- a/src/main/java/org/traccar/protocol/AuroProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AuroProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/AustinNbProtocol.java b/src/main/java/org/traccar/protocol/AustinNbProtocol.java
index 32bfc0aae..6a68467e2 100644
--- a/src/main/java/org/traccar/protocol/AustinNbProtocol.java
+++ b/src/main/java/org/traccar/protocol/AustinNbProtocol.java
@@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class AustinNbProtocol extends BaseProtocol {
- public AustinNbProtocol() {
- addServer(new TrackerServer(true, getName()) {
+ @Inject
+ public AustinNbProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new AustinNbProtocolDecoder(AustinNbProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/AustinNbProtocolDecoder.java b/src/main/java/org/traccar/protocol/AustinNbProtocolDecoder.java
index dc6f3d280..b07b94e20 100644
--- a/src/main/java/org/traccar/protocol/AustinNbProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AustinNbProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,14 +17,13 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.model.Position;
import java.net.SocketAddress;
-import java.util.TimeZone;
import java.util.regex.Pattern;
public class AustinNbProtocolDecoder extends BaseProtocolDecoder {
@@ -64,7 +63,7 @@ public class AustinNbProtocolDecoder extends BaseProtocolDecoder {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- position.setTime(parser.nextDateTime(Parser.DateTimeFormat.YMD_HMS, TimeZone.getDefault().getID()));
+ position.setTime(parser.nextDateTime());
position.setValid(true);
position.setLatitude(Double.parseDouble(parser.next().replace(',', '.')));
diff --git a/src/main/java/org/traccar/protocol/AutoFonProtocol.java b/src/main/java/org/traccar/protocol/AutoFonProtocol.java
index 08b5edc7d..0566b1da6 100644
--- a/src/main/java/org/traccar/protocol/AutoFonProtocol.java
+++ b/src/main/java/org/traccar/protocol/AutoFonProtocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class AutoFonProtocol extends BaseProtocol {
- public AutoFonProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public AutoFonProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new AutoFonFrameDecoder());
pipeline.addLast(new AutoFonProtocolDecoder(AutoFonProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/AutoFonProtocolDecoder.java b/src/main/java/org/traccar/protocol/AutoFonProtocolDecoder.java
index aa05ca2d7..dd6a0e33c 100644
--- a/src/main/java/org/traccar/protocol/AutoFonProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AutoFonProtocolDecoder.java
@@ -21,7 +21,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/AutoGradeProtocol.java b/src/main/java/org/traccar/protocol/AutoGradeProtocol.java
index c6dbb681e..bc80e473a 100644
--- a/src/main/java/org/traccar/protocol/AutoGradeProtocol.java
+++ b/src/main/java/org/traccar/protocol/AutoGradeProtocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class AutoGradeProtocol extends BaseProtocol {
- public AutoGradeProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public AutoGradeProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ')'));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/AutoGradeProtocolDecoder.java b/src/main/java/org/traccar/protocol/AutoGradeProtocolDecoder.java
index 5052450b5..f52ac81c9 100644
--- a/src/main/java/org/traccar/protocol/AutoGradeProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AutoGradeProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
diff --git a/src/main/java/org/traccar/protocol/AutoTrackProtocol.java b/src/main/java/org/traccar/protocol/AutoTrackProtocol.java
index 6aa7558bf..80255d3e9 100644
--- a/src/main/java/org/traccar/protocol/AutoTrackProtocol.java
+++ b/src/main/java/org/traccar/protocol/AutoTrackProtocol.java
@@ -19,14 +19,18 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import java.nio.ByteOrder;
+import javax.inject.Inject;
+
public class AutoTrackProtocol extends BaseProtocol {
- public AutoTrackProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public AutoTrackProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 5, 2, 2, 0, true));
pipeline.addLast(new AutoTrackProtocolDecoder(AutoTrackProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/AutoTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/AutoTrackProtocolDecoder.java
index da7f6b5a6..c072e55d0 100644
--- a/src/main/java/org/traccar/protocol/AutoTrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AutoTrackProtocolDecoder.java
@@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Checksum;
diff --git a/src/main/java/org/traccar/protocol/AvemaProtocol.java b/src/main/java/org/traccar/protocol/AvemaProtocol.java
index dbfab4dea..b35a447ff 100644
--- a/src/main/java/org/traccar/protocol/AvemaProtocol.java
+++ b/src/main/java/org/traccar/protocol/AvemaProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class AvemaProtocol extends BaseProtocol {
- public AvemaProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public AvemaProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/AvemaProtocolDecoder.java b/src/main/java/org/traccar/protocol/AvemaProtocolDecoder.java
index 37836ad5f..0793975df 100644
--- a/src/main/java/org/traccar/protocol/AvemaProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AvemaProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/Avl301Protocol.java b/src/main/java/org/traccar/protocol/Avl301Protocol.java
index 71fc7cb26..c4a0affdc 100644
--- a/src/main/java/org/traccar/protocol/Avl301Protocol.java
+++ b/src/main/java/org/traccar/protocol/Avl301Protocol.java
@@ -19,13 +19,17 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class Avl301Protocol extends BaseProtocol {
- public Avl301Protocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public Avl301Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(256, 2, 1, -3, 0));
pipeline.addLast(new Avl301ProtocolDecoder(Avl301Protocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/Avl301ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Avl301ProtocolDecoder.java
index f6b7db2d6..8f036fc29 100644
--- a/src/main/java/org/traccar/protocol/Avl301ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Avl301ProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
@@ -125,7 +125,7 @@ public class Avl301ProtocolDecoder extends BaseProtocolDecoder {
}
position.setNetwork(new Network(
- CellTower.fromLacCid(buf.readUnsignedShort(), buf.readUnsignedMedium())));
+ CellTower.fromLacCid(getConfig(), buf.readUnsignedShort(), buf.readUnsignedMedium())));
position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
int flags = buf.readUnsignedByte();
diff --git a/src/main/java/org/traccar/protocol/B2316Protocol.java b/src/main/java/org/traccar/protocol/B2316Protocol.java
index 7f08870ce..582be0b56 100644
--- a/src/main/java/org/traccar/protocol/B2316Protocol.java
+++ b/src/main/java/org/traccar/protocol/B2316Protocol.java
@@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class B2316Protocol extends BaseProtocol {
- public B2316Protocol() {
- addServer(new TrackerServer(true, getName()) {
+ @Inject
+ public B2316Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new B2316ProtocolDecoder(B2316Protocol.this));
diff --git a/src/main/java/org/traccar/protocol/B2316ProtocolDecoder.java b/src/main/java/org/traccar/protocol/B2316ProtocolDecoder.java
index 854107a20..635806b2d 100644
--- a/src/main/java/org/traccar/protocol/B2316ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/B2316ProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.model.CellTower;
import org.traccar.model.Network;
@@ -116,8 +116,9 @@ public class B2316ProtocolDecoder extends BaseProtocolDecoder {
String[] points = item.getString("wi").split(";");
for (String point : points) {
String[] values = point.split(",");
+ String mac = values[0].replaceAll("(..)", "$1:");
network.addWifiAccessPoint(WifiAccessPoint.from(
- values[0].replaceAll("(..)", "$1:"), Integer.parseInt(values[1])));
+ mac.substring(0, mac.length() - 1), Integer.parseInt(values[1])));
}
}
diff --git a/src/main/java/org/traccar/protocol/BceProtocol.java b/src/main/java/org/traccar/protocol/BceProtocol.java
index c5e1dd04c..31fb1bd83 100644
--- a/src/main/java/org/traccar/protocol/BceProtocol.java
+++ b/src/main/java/org/traccar/protocol/BceProtocol.java
@@ -18,16 +18,20 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class BceProtocol extends BaseProtocol {
- public BceProtocol() {
+ @Inject
+ public BceProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_OUTPUT_CONTROL);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new BceFrameDecoder());
pipeline.addLast(new BceProtocolEncoder(BceProtocol.this));
pipeline.addLast(new BceProtocolDecoder(BceProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/BceProtocolDecoder.java b/src/main/java/org/traccar/protocol/BceProtocolDecoder.java
index c1a69981d..2c9459584 100644
--- a/src/main/java/org/traccar/protocol/BceProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/BceProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
@@ -90,11 +90,13 @@ public class BceProtocolDecoder extends BaseProtocolDecoder {
}
if (BitUtil.check(mask, 14)) {
- position.setNetwork(new Network(CellTower.from(
- buf.readUnsignedShortLE(), buf.readUnsignedByte(),
- buf.readUnsignedShortLE(), buf.readUnsignedShortLE(),
- buf.readUnsignedByte())));
- buf.readUnsignedByte();
+ int mcc = buf.readUnsignedShortLE();
+ int mnc = buf.readUnsignedByte();
+ int lac = buf.readUnsignedShortLE();
+ int cid = buf.readUnsignedShortLE();
+ buf.readUnsignedByte(); // time advance
+ int rssi = -buf.readUnsignedByte();
+ position.setNetwork(new Network(CellTower.from(mcc, mnc, lac, cid, rssi)));
}
}
diff --git a/src/main/java/org/traccar/protocol/BlackKiteProtocol.java b/src/main/java/org/traccar/protocol/BlackKiteProtocol.java
index 617a24d7a..3859a9273 100644
--- a/src/main/java/org/traccar/protocol/BlackKiteProtocol.java
+++ b/src/main/java/org/traccar/protocol/BlackKiteProtocol.java
@@ -19,13 +19,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class BlackKiteProtocol extends BaseProtocol {
- public BlackKiteProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public BlackKiteProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new GalileoFrameDecoder());
pipeline.addLast(new BlackKiteProtocolDecoder(BlackKiteProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/BlackKiteProtocolDecoder.java b/src/main/java/org/traccar/protocol/BlackKiteProtocolDecoder.java
index 474ceabdc..64fc439c4 100644
--- a/src/main/java/org/traccar/protocol/BlackKiteProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/BlackKiteProtocolDecoder.java
@@ -20,7 +20,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/BlueProtocol.java b/src/main/java/org/traccar/protocol/BlueProtocol.java
index d5dc5c421..da195f438 100644
--- a/src/main/java/org/traccar/protocol/BlueProtocol.java
+++ b/src/main/java/org/traccar/protocol/BlueProtocol.java
@@ -19,13 +19,17 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class BlueProtocol extends BaseProtocol {
- public BlueProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public BlueProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 1, 2, -2, 0));
pipeline.addLast(new BlueProtocolDecoder(BlueProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/BlueProtocolDecoder.java b/src/main/java/org/traccar/protocol/BlueProtocolDecoder.java
index f35ac6fbe..db59c564d 100644
--- a/src/main/java/org/traccar/protocol/BlueProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/BlueProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/BoxProtocol.java b/src/main/java/org/traccar/protocol/BoxProtocol.java
index dfea15938..dc6852d50 100644
--- a/src/main/java/org/traccar/protocol/BoxProtocol.java
+++ b/src/main/java/org/traccar/protocol/BoxProtocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class BoxProtocol extends BaseProtocol {
- public BoxProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public BoxProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '\r'));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/BoxProtocolDecoder.java b/src/main/java/org/traccar/protocol/BoxProtocolDecoder.java
index 853fa8f81..8e92b69fb 100644
--- a/src/main/java/org/traccar/protocol/BoxProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/BoxProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/BstplProtocol.java b/src/main/java/org/traccar/protocol/BstplProtocol.java
new file mode 100644
index 000000000..dde14a2ca
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/BstplProtocol.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.CharacterDelimiterFrameDecoder;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
+
+public class BstplProtocol extends BaseProtocol {
+
+ @Inject
+ public BstplProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '#'));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new BstplProtocolDecoder(BstplProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/BstplProtocolDecoder.java b/src/main/java/org/traccar/protocol/BstplProtocolDecoder.java
new file mode 100644
index 000000000..15c114642
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/BstplProtocolDecoder.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.Protocol;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+import org.traccar.session.DeviceSession;
+
+import java.net.SocketAddress;
+import java.util.regex.Pattern;
+
+public class BstplProtocolDecoder extends BaseProtocolDecoder {
+
+ public BstplProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("BSTPL$") // header
+ .number("(d),") // type
+ .expression("([^,]+),") // device id
+ .expression("([AV]),") // validity
+ .number("(dd)(dd)(dd),") // date (ddmmyy)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(d+.d+),([0NS]),") // latitude
+ .number("(d+.d+),([0EW]),") // longitude
+ .number("(d+),") // speed
+ .number("(d+),") // odometer
+ .number("(d+),") // course
+ .number("(d+),") // satellites
+ .number("([01]),") // box open
+ .number("(d+),") // rssi
+ .number("([01]),") // charge
+ .number("([01]),") // ignition
+ .number("([01]),") // engine
+ .number("([01]),") // locked
+ .number("(d+.d+),") // adc
+ .number("d+,") // reserved
+ .number("(d+.d+),") // battery
+ .expression("([^,]+),") // firmware
+ .number("([^,]+),") // iccid
+ .number("(d+.d+)") // power
+ .compile();
+
+ private String decodeAlarm(int value) {
+ switch (value) {
+ case 4:
+ return Position.ALARM_LOW_BATTERY;
+ case 5:
+ return Position.ALARM_ACCELERATION;
+ case 6:
+ return Position.ALARM_BRAKING;
+ case 7:
+ return Position.ALARM_OVERSPEED;
+ case 9:
+ return Position.ALARM_SOS;
+ default:
+ return null;
+ }
+ }
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ Parser parser = new Parser(PATTERN, (String) msg);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ int type = parser.nextInt();
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.set(Position.KEY_ALARM, decodeAlarm(type));
+
+ position.setValid(parser.next().equals("A"));
+ position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+ position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
+ position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextInt()));
+
+ position.set(Position.KEY_ODOMETER, parser.nextInt() * 1000L);
+
+ position.setCourse(parser.nextInt());
+
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+
+ boolean boxOpen = parser.nextInt() > 0;
+ if (type == 8 && boxOpen) {
+ position.set(Position.KEY_ALARM, Position.ALARM_TAMPERING);
+ }
+ position.set("boxOpen", boxOpen);
+
+ position.set(Position.KEY_RSSI, parser.nextInt());
+
+ boolean charge = parser.nextInt() > 0;
+ if (type == 3) {
+ position.set(Position.KEY_ALARM, charge ? Position.ALARM_POWER_RESTORED : Position.ALARM_POWER_CUT);
+ }
+ position.set(Position.KEY_CHARGE, charge);
+
+ position.set(Position.KEY_IGNITION, parser.nextInt() > 0);
+ position.set("engine", parser.nextInt() > 0);
+ position.set(Position.KEY_BLOCKED, parser.nextInt() > 0);
+ position.set(Position.PREFIX_ADC + 1, parser.nextDouble());
+ position.set(Position.KEY_BATTERY, parser.nextDouble());
+ position.set(Position.KEY_ICCID, parser.next());
+ position.set(Position.KEY_POWER, parser.nextDouble());
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/C2stekProtocol.java b/src/main/java/org/traccar/protocol/C2stekProtocol.java
index 804621fd3..5cd8ef4fd 100644
--- a/src/main/java/org/traccar/protocol/C2stekProtocol.java
+++ b/src/main/java/org/traccar/protocol/C2stekProtocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class C2stekProtocol extends BaseProtocol {
- public C2stekProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public C2stekProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, false, "$AP"));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/C2stekProtocolDecoder.java b/src/main/java/org/traccar/protocol/C2stekProtocolDecoder.java
index 83e62ff86..aef158fc7 100644
--- a/src/main/java/org/traccar/protocol/C2stekProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/C2stekProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
@@ -49,10 +49,12 @@ public class C2stekProtocolDecoder extends BaseProtocolDecoder {
.number("(-?d+.d+)#") // altitude
.number("(d+)#") // battery
.number("d+#") // geo area alarm
- .number("(x+)#") // alarm
- .number("([01])") // armed
+ .number("(x+)") // alarm
+ .groupBegin()
+ .number("#([01])?") // armed
.number("([01])") // door
.number("([01])#") // ignition
+ .groupEnd("?")
.any()
.text("$AP")
.compile();
@@ -111,9 +113,13 @@ public class C2stekProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_BATTERY, parser.nextInt() * 0.001);
position.set(Position.KEY_ALARM, decodeAlarm(parser.nextHexInt()));
- position.set(Position.KEY_ARMED, parser.nextInt() > 0);
- position.set(Position.KEY_DOOR, parser.nextInt() > 0);
- position.set(Position.KEY_IGNITION, parser.nextInt() > 0);
+ if (parser.hasNext()) {
+ position.set(Position.KEY_ARMED, parser.nextInt() > 0);
+ }
+ if (parser.hasNext(2)) {
+ position.set(Position.KEY_DOOR, parser.nextInt() > 0);
+ position.set(Position.KEY_IGNITION, parser.nextInt() > 0);
+ }
return position;
}
diff --git a/src/main/java/org/traccar/protocol/CalAmpProtocol.java b/src/main/java/org/traccar/protocol/CalAmpProtocol.java
index 232e72a8c..d67308cf2 100644
--- a/src/main/java/org/traccar/protocol/CalAmpProtocol.java
+++ b/src/main/java/org/traccar/protocol/CalAmpProtocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class CalAmpProtocol extends BaseProtocol {
- public CalAmpProtocol() {
- addServer(new TrackerServer(true, getName()) {
+ @Inject
+ public CalAmpProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CalAmpProtocolDecoder(CalAmpProtocol.this));
}
});
diff --git a/src/main/java/org/traccar/protocol/CalAmpProtocolDecoder.java b/src/main/java/org/traccar/protocol/CalAmpProtocolDecoder.java
index 59b1fdf21..57f9c69ae 100644
--- a/src/main/java/org/traccar/protocol/CalAmpProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/CalAmpProtocolDecoder.java
@@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/CarTrackProtocol.java b/src/main/java/org/traccar/protocol/CarTrackProtocol.java
index e340fba25..0538aad72 100644
--- a/src/main/java/org/traccar/protocol/CarTrackProtocol.java
+++ b/src/main/java/org/traccar/protocol/CarTrackProtocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class CarTrackProtocol extends BaseProtocol {
- public CarTrackProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public CarTrackProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "##"));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/CarTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/CarTrackProtocolDecoder.java
index ce3345826..3f5418549 100644
--- a/src/main/java/org/traccar/protocol/CarTrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/CarTrackProtocolDecoder.java
@@ -18,7 +18,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/CarcellProtocol.java b/src/main/java/org/traccar/protocol/CarcellProtocol.java
index f08ab3bd9..832d9bb2d 100644
--- a/src/main/java/org/traccar/protocol/CarcellProtocol.java
+++ b/src/main/java/org/traccar/protocol/CarcellProtocol.java
@@ -21,17 +21,21 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class CarcellProtocol extends BaseProtocol {
- public CarcellProtocol() {
+ @Inject
+ public CarcellProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_ENGINE_STOP,
Command.TYPE_ENGINE_RESUME);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '\r'));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/CarcellProtocolDecoder.java b/src/main/java/org/traccar/protocol/CarcellProtocolDecoder.java
index ec640ba71..54ae068fb 100644
--- a/src/main/java/org/traccar/protocol/CarcellProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/CarcellProtocolDecoder.java
@@ -20,7 +20,7 @@ import java.util.regex.Pattern;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.Parser.CoordinateFormat;
diff --git a/src/main/java/org/traccar/protocol/CarscopProtocol.java b/src/main/java/org/traccar/protocol/CarscopProtocol.java
index 2c754a97f..a4413af28 100644
--- a/src/main/java/org/traccar/protocol/CarscopProtocol.java
+++ b/src/main/java/org/traccar/protocol/CarscopProtocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class CarscopProtocol extends BaseProtocol {
- public CarscopProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public CarscopProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '^'));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/CarscopProtocolDecoder.java b/src/main/java/org/traccar/protocol/CarscopProtocolDecoder.java
index 161666adc..f13a1d0eb 100644
--- a/src/main/java/org/traccar/protocol/CarscopProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/CarscopProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/CastelProtocol.java b/src/main/java/org/traccar/protocol/CastelProtocol.java
index 44c52d68f..9323b1503 100644
--- a/src/main/java/org/traccar/protocol/CastelProtocol.java
+++ b/src/main/java/org/traccar/protocol/CastelProtocol.java
@@ -19,26 +19,30 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
import java.nio.ByteOrder;
+import javax.inject.Inject;
+
public class CastelProtocol extends BaseProtocol {
- public CastelProtocol() {
+ @Inject
+ public CastelProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_ENGINE_STOP,
Command.TYPE_ENGINE_RESUME);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 2, 2, -4, 0, true));
pipeline.addLast(new CastelProtocolEncoder(CastelProtocol.this));
pipeline.addLast(new CastelProtocolDecoder(CastelProtocol.this));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CastelProtocolEncoder(CastelProtocol.this));
pipeline.addLast(new CastelProtocolDecoder(CastelProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/CastelProtocolDecoder.java b/src/main/java/org/traccar/protocol/CastelProtocolDecoder.java
index 23401b5ee..4aa65245b 100644
--- a/src/main/java/org/traccar/protocol/CastelProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/CastelProtocolDecoder.java
@@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
@@ -160,6 +160,9 @@ public class CastelProtocolDecoder extends BaseProtocolDecoder {
for (int i = 0; i < count; i++) {
int value;
+ if (!PID_LENGTH_MAP.containsKey(pids[i])) {
+ throw new RuntimeException(String.format("Unknown PID 0x%02x", pids[i]));
+ }
switch (PID_LENGTH_MAP.get(pids[i])) {
case 1:
value = buf.readUnsignedByte();
@@ -443,7 +446,7 @@ public class CastelProtocolDecoder extends BaseProtocolDecoder {
decodeStat(position, buf);
position.setNetwork(new Network(
- CellTower.fromLacCid(buf.readUnsignedShortLE(), buf.readUnsignedShortLE())));
+ CellTower.fromLacCid(getConfig(), buf.readUnsignedShortLE(), buf.readUnsignedShortLE())));
return position;
@@ -499,7 +502,7 @@ public class CastelProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedByte(); // additional flags
position.setNetwork(new Network(
- CellTower.fromLacCid(buf.readUnsignedShortLE(), buf.readUnsignedShortLE())));
+ CellTower.fromLacCid(getConfig(), buf.readUnsignedShortLE(), buf.readUnsignedShortLE())));
positions.add(position);
}
diff --git a/src/main/java/org/traccar/protocol/CastelProtocolEncoder.java b/src/main/java/org/traccar/protocol/CastelProtocolEncoder.java
index dc694da28..61dde3e80 100644
--- a/src/main/java/org/traccar/protocol/CastelProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/CastelProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,10 +18,9 @@ package org.traccar.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.traccar.BaseProtocolEncoder;
-import org.traccar.Context;
+import org.traccar.Protocol;
import org.traccar.helper.Checksum;
import org.traccar.model.Command;
-import org.traccar.Protocol;
import java.nio.charset.StandardCharsets;
@@ -34,7 +33,7 @@ public class CastelProtocolEncoder extends BaseProtocolEncoder {
private ByteBuf encodeContent(long deviceId, short type, ByteBuf content) {
ByteBuf buf = Unpooled.buffer(0);
- String uniqueId = Context.getIdentityManager().getById(deviceId).getUniqueId();
+ String uniqueId = getUniqueId(deviceId);
buf.writeByte('@');
buf.writeByte('@');
diff --git a/src/main/java/org/traccar/protocol/CautelaProtocol.java b/src/main/java/org/traccar/protocol/CautelaProtocol.java
index 452bdf8d4..d0ca35ef1 100644
--- a/src/main/java/org/traccar/protocol/CautelaProtocol.java
+++ b/src/main/java/org/traccar/protocol/CautelaProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class CautelaProtocol extends BaseProtocol {
- public CautelaProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public CautelaProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/CautelaProtocolDecoder.java b/src/main/java/org/traccar/protocol/CautelaProtocolDecoder.java
index bddf19b41..37f733ac1 100644
--- a/src/main/java/org/traccar/protocol/CautelaProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/CautelaProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/CellocatorProtocol.java b/src/main/java/org/traccar/protocol/CellocatorProtocol.java
index d910877cf..3287928c7 100644
--- a/src/main/java/org/traccar/protocol/CellocatorProtocol.java
+++ b/src/main/java/org/traccar/protocol/CellocatorProtocol.java
@@ -18,24 +18,28 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class CellocatorProtocol extends BaseProtocol {
- public CellocatorProtocol() {
+ @Inject
+ public CellocatorProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_OUTPUT_CONTROL);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CellocatorFrameDecoder());
pipeline.addLast(new CellocatorProtocolEncoder(CellocatorProtocol.this));
pipeline.addLast(new CellocatorProtocolDecoder(CellocatorProtocol.this));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CellocatorProtocolEncoder(CellocatorProtocol.this));
pipeline.addLast(new CellocatorProtocolDecoder(CellocatorProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java b/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java
index 09bd3572f..3573a95ca 100644
--- a/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
@@ -120,13 +120,15 @@ public class CellocatorProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedByte(); // operator / configuration flags
buf.readUnsignedByte(); // reason data
- position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
+ short event = buf.readUnsignedByte();
+ position.set(Position.KEY_ALARM, decodeAlarm(event));
+ position.set(Position.KEY_EVENT, event);
position.set("mode", buf.readUnsignedByte());
- long input = buf.readUnsignedIntLE();
+ long input = buf.readUnsignedInt();
+ position.set(Position.KEY_IGNITION, BitUtil.check(input, 3 * 8 + 5));
position.set(Position.KEY_DOOR, BitUtil.check(input, 3 * 8));
- position.set(Position.KEY_IGNITION, BitUtil.check(input, 2 * 8 + 7));
position.set(Position.KEY_CHARGE, BitUtil.check(input, 7));
position.set(Position.KEY_INPUT, input);
diff --git a/src/main/java/org/traccar/protocol/CguardProtocol.java b/src/main/java/org/traccar/protocol/CguardProtocol.java
index 9157ca35c..caf0aad42 100644
--- a/src/main/java/org/traccar/protocol/CguardProtocol.java
+++ b/src/main/java/org/traccar/protocol/CguardProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class CguardProtocol extends BaseProtocol {
- public CguardProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public CguardProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/CguardProtocolDecoder.java b/src/main/java/org/traccar/protocol/CguardProtocolDecoder.java
index d934921f1..90f8e0caf 100644
--- a/src/main/java/org/traccar/protocol/CguardProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/CguardProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/CityeasyProtocol.java b/src/main/java/org/traccar/protocol/CityeasyProtocol.java
index 8ab4ce93a..9656b284b 100644
--- a/src/main/java/org/traccar/protocol/CityeasyProtocol.java
+++ b/src/main/java/org/traccar/protocol/CityeasyProtocol.java
@@ -19,19 +19,23 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class CityeasyProtocol extends BaseProtocol {
- public CityeasyProtocol() {
+ @Inject
+ public CityeasyProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_POSITION_SINGLE,
Command.TYPE_POSITION_PERIODIC,
Command.TYPE_POSITION_STOP,
Command.TYPE_SET_TIMEZONE);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 2, 2, -4, 0));
pipeline.addLast(new CityeasyProtocolEncoder(CityeasyProtocol.this));
pipeline.addLast(new CityeasyProtocolDecoder(CityeasyProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/CityeasyProtocolDecoder.java b/src/main/java/org/traccar/protocol/CityeasyProtocolDecoder.java
index 9c4c7e11d..1b5eb55d4 100644
--- a/src/main/java/org/traccar/protocol/CityeasyProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/CityeasyProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Checksum;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/ContinentalProtocol.java b/src/main/java/org/traccar/protocol/ContinentalProtocol.java
index bc7928fba..06e93d79d 100644
--- a/src/main/java/org/traccar/protocol/ContinentalProtocol.java
+++ b/src/main/java/org/traccar/protocol/ContinentalProtocol.java
@@ -19,13 +19,17 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class ContinentalProtocol extends BaseProtocol {
- public ContinentalProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public ContinentalProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 2, 2, -4, 0));
pipeline.addLast(new ContinentalProtocolDecoder(ContinentalProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/ContinentalProtocolDecoder.java b/src/main/java/org/traccar/protocol/ContinentalProtocolDecoder.java
index 471afa0d6..280871e1e 100644
--- a/src/main/java/org/traccar/protocol/ContinentalProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ContinentalProtocolDecoder.java
@@ -18,7 +18,7 @@ package org.traccar.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.UnitsConverter;
diff --git a/src/main/java/org/traccar/protocol/CradlepointProtocol.java b/src/main/java/org/traccar/protocol/CradlepointProtocol.java
index 4a09e0311..7f201a31d 100644
--- a/src/main/java/org/traccar/protocol/CradlepointProtocol.java
+++ b/src/main/java/org/traccar/protocol/CradlepointProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class CradlepointProtocol extends BaseProtocol {
- public CradlepointProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public CradlepointProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/CradlepointProtocolDecoder.java b/src/main/java/org/traccar/protocol/CradlepointProtocolDecoder.java
index a282131ce..924603291 100644
--- a/src/main/java/org/traccar/protocol/CradlepointProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/CradlepointProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/DingtekProtocol.java b/src/main/java/org/traccar/protocol/DingtekProtocol.java
index cf2a6c0f5..e9466b7e8 100644
--- a/src/main/java/org/traccar/protocol/DingtekProtocol.java
+++ b/src/main/java/org/traccar/protocol/DingtekProtocol.java
@@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class DingtekProtocol extends BaseProtocol {
- public DingtekProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public DingtekProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new DingtekFrameDecoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/DingtekProtocolDecoder.java b/src/main/java/org/traccar/protocol/DingtekProtocolDecoder.java
index 98fe4b7b3..580741ec9 100644
--- a/src/main/java/org/traccar/protocol/DingtekProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/DingtekProtocolDecoder.java
@@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.DataConverter;
import org.traccar.model.Position;
diff --git a/src/main/java/org/traccar/protocol/DishaProtocol.java b/src/main/java/org/traccar/protocol/DishaProtocol.java
index 38f49cc05..f83b8349a 100644
--- a/src/main/java/org/traccar/protocol/DishaProtocol.java
+++ b/src/main/java/org/traccar/protocol/DishaProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class DishaProtocol extends BaseProtocol {
- public DishaProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public DishaProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/DishaProtocolDecoder.java b/src/main/java/org/traccar/protocol/DishaProtocolDecoder.java
index 3223988ab..1327e7a6c 100644
--- a/src/main/java/org/traccar/protocol/DishaProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/DishaProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/DmtHttpProtocol.java b/src/main/java/org/traccar/protocol/DmtHttpProtocol.java
index 34568128f..0dab26cda 100644
--- a/src/main/java/org/traccar/protocol/DmtHttpProtocol.java
+++ b/src/main/java/org/traccar/protocol/DmtHttpProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class DmtHttpProtocol extends BaseProtocol {
- public DmtHttpProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public DmtHttpProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new HttpResponseEncoder());
pipeline.addLast(new HttpRequestDecoder());
pipeline.addLast(new HttpObjectAggregator(65535));
diff --git a/src/main/java/org/traccar/protocol/DmtHttpProtocolDecoder.java b/src/main/java/org/traccar/protocol/DmtHttpProtocolDecoder.java
index 15cf84a5f..807850778 100644
--- a/src/main/java/org/traccar/protocol/DmtHttpProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/DmtHttpProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.channel.Channel;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import org.traccar.BaseHttpProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.UnitsConverter;
@@ -174,21 +174,25 @@ public class DmtHttpProtocolDecoder extends BaseHttpProtocolDecoder {
position.set(Position.KEY_INDEX, root.getInt("sqn"));
position.set(Position.KEY_EVENT, root.getInt("reason"));
- JsonArray analogues = root.getJsonArray("analogues");
- for (int i = 0; i < analogues.size(); i++) {
- JsonObject adc = analogues.getJsonObject(i);
- position.set(Position.PREFIX_ADC + adc.getInt("id"), adc.getInt("val"));
+ if (root.containsKey("analogues")) {
+ JsonArray analogues = root.getJsonArray("analogues");
+ for (int i = 0; i < analogues.size(); i++) {
+ JsonObject adc = analogues.getJsonObject(i);
+ position.set(Position.PREFIX_ADC + adc.getInt("id"), adc.getInt("val"));
+ }
}
- int input = root.getInt("inputs");
- int output = root.getInt("outputs");
- int status = root.getInt("status");
-
- position.set(Position.KEY_IGNITION, BitUtil.check(input, 0));
-
- position.set(Position.KEY_INPUT, input);
- position.set(Position.KEY_OUTPUT, output);
- position.set(Position.KEY_STATUS, status);
+ if (root.containsKey("inputs")) {
+ int input = root.getInt("inputs");
+ position.set(Position.KEY_IGNITION, BitUtil.check(input, 0));
+ position.set(Position.KEY_INPUT, input);
+ }
+ if (root.containsKey("outputs")) {
+ position.set(Position.KEY_OUTPUT, root.getInt("outputs"));
+ }
+ if (root.containsKey("status")) {
+ position.set(Position.KEY_STATUS, root.getInt("status"));
+ }
if (root.containsKey("counters")) {
JsonArray counters = root.getJsonArray("counters");
diff --git a/src/main/java/org/traccar/protocol/DmtProtocol.java b/src/main/java/org/traccar/protocol/DmtProtocol.java
index 78a5243c0..de56c9372 100644
--- a/src/main/java/org/traccar/protocol/DmtProtocol.java
+++ b/src/main/java/org/traccar/protocol/DmtProtocol.java
@@ -19,14 +19,18 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import java.nio.ByteOrder;
+import javax.inject.Inject;
+
public class DmtProtocol extends BaseProtocol {
- public DmtProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public DmtProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 3, 2, 0, 0, true));
pipeline.addLast(new DmtProtocolDecoder(DmtProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/DmtProtocolDecoder.java b/src/main/java/org/traccar/protocol/DmtProtocolDecoder.java
index 96b06557a..320aa1b60 100644
--- a/src/main/java/org/traccar/protocol/DmtProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/DmtProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
@@ -184,9 +184,9 @@ public class DmtProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_IGNITION, BitUtil.check(input, 0));
- if (!BitUtil.check(input, 1)) {
+ if (!BitUtil.check(status, 1)) {
position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY);
- } else if (BitUtil.check(input, 6)) {
+ } else if (BitUtil.check(status, 6)) {
position.set(Position.KEY_ALARM, Position.ALARM_TAMPERING);
}
diff --git a/src/main/java/org/traccar/protocol/DolphinProtocol.java b/src/main/java/org/traccar/protocol/DolphinProtocol.java
index 07c827e18..ed627be78 100644
--- a/src/main/java/org/traccar/protocol/DolphinProtocol.java
+++ b/src/main/java/org/traccar/protocol/DolphinProtocol.java
@@ -19,15 +19,19 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import java.nio.ByteOrder;
+import javax.inject.Inject;
+
public class DolphinProtocol extends BaseProtocol {
- public DolphinProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public DolphinProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 4096, 20, 4, 4, 0, true));
pipeline.addLast(new DolphinProtocolDecoder(DolphinProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/DolphinProtocolDecoder.java b/src/main/java/org/traccar/protocol/DolphinProtocolDecoder.java
index d509b3ec0..b43635a52 100644
--- a/src/main/java/org/traccar/protocol/DolphinProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/DolphinProtocolDecoder.java
@@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.UnitsConverter;
diff --git a/src/main/java/org/traccar/protocol/Dsf22FrameDecoder.java b/src/main/java/org/traccar/protocol/Dsf22FrameDecoder.java
new file mode 100644
index 000000000..388c97f85
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Dsf22FrameDecoder.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2021 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
+
+public class Dsf22FrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ if (buf.readableBytes() < 21) {
+ return null;
+ }
+
+ int count = buf.getUnsignedByte(buf.readerIndex() + 4);
+
+ int length = 2 + 2 + 1 + count * (4 + 4 + 4 + 1 + 2 + 1);
+
+ if (buf.readableBytes() >= length) {
+ return buf.readRetainedSlice(length);
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Dsf22Protocol.java b/src/main/java/org/traccar/protocol/Dsf22Protocol.java
new file mode 100644
index 000000000..06c99b0f9
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Dsf22Protocol.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2021 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
+
+public class Dsf22Protocol extends BaseProtocol {
+
+ @Inject
+ public Dsf22Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
+ pipeline.addLast(new Dsf22FrameDecoder());
+ pipeline.addLast(new Dsf22ProtocolDecoder(Dsf22Protocol.this));
+ }
+ });
+ addServer(new TrackerServer(config, getName(), true) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
+ pipeline.addLast(new Dsf22ProtocolDecoder(Dsf22Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Dsf22ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Dsf22ProtocolDecoder.java
new file mode 100644
index 000000000..124bbfefa
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Dsf22ProtocolDecoder.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.session.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+public class Dsf22ProtocolDecoder extends BaseProtocolDecoder {
+
+ public Dsf22ProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.skipBytes(2); // header
+
+ String id = String.valueOf(buf.readUnsignedShortLE());
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ List<Position> positions = new LinkedList<>();
+ int count = buf.readUnsignedByte();
+
+ for (int i = 0; i < count; i++) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setValid(true);
+ position.setLatitude(buf.readIntLE() / 10000000.0);
+ position.setLongitude(buf.readIntLE() / 10000000.0);
+ position.setTime(new Date(buf.readUnsignedIntLE() * 1000));
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
+
+ position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedShortLE() * 0.001);
+
+ int status = buf.readUnsignedByte();
+ position.set(Position.KEY_IGNITION, BitUtil.check(status, 0));
+ position.set(Position.PREFIX_IN + 1, BitUtil.check(status, 1));
+ position.set(Position.PREFIX_OUT + 1, BitUtil.check(status, 4));
+ position.set(Position.KEY_ALARM, BitUtil.check(status, 6) ? Position.ALARM_JAMMING : null);
+ position.set(Position.KEY_STATUS, status);
+
+ positions.add(position);
+
+ }
+
+ if (channel != null) {
+ byte[] response = {0x01};
+ channel.writeAndFlush(new NetworkMessage(Unpooled.wrappedBuffer(response), remoteAddress));
+ }
+
+ return positions;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/DualcamProtocol.java b/src/main/java/org/traccar/protocol/DualcamProtocol.java
index 04c4f2bd1..363a2c5d9 100644
--- a/src/main/java/org/traccar/protocol/DualcamProtocol.java
+++ b/src/main/java/org/traccar/protocol/DualcamProtocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class DualcamProtocol extends BaseProtocol {
- public DualcamProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public DualcamProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new DualcamFrameDecoder());
pipeline.addLast(new DualcamProtocolDecoder(DualcamProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/DualcamProtocolDecoder.java b/src/main/java/org/traccar/protocol/DualcamProtocolDecoder.java
index c64b8171f..d03f7648d 100644
--- a/src/main/java/org/traccar/protocol/DualcamProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/DualcamProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,8 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
@@ -47,7 +46,8 @@ public class DualcamProtocolDecoder extends BaseProtocolDecoder {
private String uniqueId;
private int packetCount;
private int currentPacket;
- private ByteBuf photo;
+ private boolean video;
+ private ByteBuf media;
@Override
protected Object decode(
@@ -65,13 +65,26 @@ public class DualcamProtocolDecoder extends BaseProtocolDecoder {
long settings = buf.readUnsignedInt();
if (channel != null && deviceSession != null) {
ByteBuf response = Unpooled.buffer();
- if (BitUtil.between(settings, 26, 28) > 0) {
+ if (BitUtil.between(settings, 26, 30) > 0) {
response.writeShort(MSG_FILE_REQUEST);
- String file = BitUtil.check(settings, 26) ? "%photof" : "%photor";
+ String file;
+ if (BitUtil.check(settings, 26)) {
+ video = false;
+ file = "%photof";
+ } else if (BitUtil.check(settings, 27)) {
+ video = false;
+ file = "%photor";
+ } else if (BitUtil.check(settings, 28)) {
+ video = true;
+ file = "%videof";
+ } else {
+ video = true;
+ file = "%videor";
+ }
response.writeShort(file.length());
response.writeCharSequence(file, StandardCharsets.US_ASCII);
} else {
- response.writeShort(MSG_COMPLETE);
+ response.writeShort(MSG_INIT);
}
channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
@@ -80,7 +93,7 @@ public class DualcamProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedShort(); // length
packetCount = buf.readInt();
currentPacket = 1;
- photo = Unpooled.buffer();
+ media = Unpooled.buffer();
if (channel != null) {
ByteBuf response = Unpooled.buffer();
response.writeShort(MSG_RESUME);
@@ -91,17 +104,21 @@ public class DualcamProtocolDecoder extends BaseProtocolDecoder {
break;
case MSG_DATA:
buf.readUnsignedShort(); // length
- photo.writeBytes(buf, buf.readableBytes() - 2);
+ media.writeBytes(buf, buf.readableBytes() - 2);
if (currentPacket == packetCount) {
deviceSession = getDeviceSession(channel, remoteAddress);
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
getLastLocation(position, null);
try {
- position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(uniqueId, photo, "jpg"));
+ if (video) {
+ position.set(Position.KEY_VIDEO, writeMediaFile(uniqueId, media, "h265"));
+ } else {
+ position.set(Position.KEY_IMAGE, writeMediaFile(uniqueId, media, "jpg"));
+ }
} finally {
- photo.release();
- photo = null;
+ media.release();
+ media = null;
}
if (channel != null) {
ByteBuf response = Unpooled.buffer();
diff --git a/src/main/java/org/traccar/protocol/DwayProtocol.java b/src/main/java/org/traccar/protocol/DwayProtocol.java
index 05fd8b6e7..1096c945c 100644
--- a/src/main/java/org/traccar/protocol/DwayProtocol.java
+++ b/src/main/java/org/traccar/protocol/DwayProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class DwayProtocol extends BaseProtocol {
- public DwayProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public DwayProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/DwayProtocolDecoder.java b/src/main/java/org/traccar/protocol/DwayProtocolDecoder.java
index 9b02c898e..9cf40b011 100644
--- a/src/main/java/org/traccar/protocol/DwayProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/DwayProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/EasyTrackProtocol.java b/src/main/java/org/traccar/protocol/EasyTrackProtocol.java
index 972b36077..39aa61580 100644
--- a/src/main/java/org/traccar/protocol/EasyTrackProtocol.java
+++ b/src/main/java/org/traccar/protocol/EasyTrackProtocol.java
@@ -21,19 +21,23 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class EasyTrackProtocol extends BaseProtocol {
- public EasyTrackProtocol() {
+ @Inject
+ public EasyTrackProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_ENGINE_STOP,
Command.TYPE_ENGINE_RESUME,
Command.TYPE_ALARM_ARM,
Command.TYPE_ALARM_DISARM);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "#\r\n", "#", "\r\n"));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java
index 4fcc48944..805cf1197 100644
--- a/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/EelinkProtocol.java b/src/main/java/org/traccar/protocol/EelinkProtocol.java
index 8a055d643..35fd4fe65 100644
--- a/src/main/java/org/traccar/protocol/EelinkProtocol.java
+++ b/src/main/java/org/traccar/protocol/EelinkProtocol.java
@@ -19,28 +19,32 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class EelinkProtocol extends BaseProtocol {
- public EelinkProtocol() {
+ @Inject
+ public EelinkProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_CUSTOM,
Command.TYPE_POSITION_SINGLE,
Command.TYPE_ENGINE_STOP,
Command.TYPE_ENGINE_RESUME,
Command.TYPE_REBOOT_DEVICE);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 3, 2));
pipeline.addLast(new EelinkProtocolEncoder(EelinkProtocol.this, false));
pipeline.addLast(new EelinkProtocolDecoder(EelinkProtocol.this));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new EelinkProtocolEncoder(EelinkProtocol.this, true));
pipeline.addLast(new EelinkProtocolDecoder(EelinkProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java b/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java
index 068b19cbc..941b10fef 100644
--- a/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,7 +21,7 @@ import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.socket.DatagramChannel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
@@ -31,6 +31,7 @@ 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;
@@ -192,30 +193,67 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder {
getLastLocation(position, position.getDeviceTime());
}
+ Network network = new Network();
+
+ int mcc = 0;
+ int mnc = 0;
if (BitUtil.check(flags, 1)) {
- position.setNetwork(new Network(CellTower.from(
- buf.readUnsignedShort(), buf.readUnsignedShort(),
- buf.readUnsignedShort(), buf.readUnsignedInt(), buf.readUnsignedByte())));
+ mcc = buf.readUnsignedShort();
+ mnc = buf.readUnsignedShort();
+ network.addCellTower(CellTower.from(
+ mcc, mnc, buf.readUnsignedShort(), buf.readUnsignedInt(), buf.readUnsignedByte()));
}
if (BitUtil.check(flags, 2)) {
- buf.skipBytes(7); // bsid1
+ network.addCellTower(CellTower.from(
+ mcc, mnc, buf.readUnsignedShort(), buf.readUnsignedInt(), buf.readUnsignedByte()));
}
if (BitUtil.check(flags, 3)) {
- buf.skipBytes(7); // bsid2
+ network.addCellTower(CellTower.from(
+ mcc, mnc, buf.readUnsignedShort(), buf.readUnsignedInt(), buf.readUnsignedByte()));
}
if (BitUtil.check(flags, 4)) {
- buf.skipBytes(7); // bss0
+ String mac = ByteBufUtil.hexDump(buf.readSlice(6)).replaceAll("(..)", "$1:");
+ network.addWifiAccessPoint(WifiAccessPoint.from(
+ mac.substring(0, mac.length() - 1), buf.readUnsignedByte()));
}
if (BitUtil.check(flags, 5)) {
- buf.skipBytes(7); // bss1
+ String mac = ByteBufUtil.hexDump(buf.readSlice(6)).replaceAll("(..)", "$1:");
+ network.addWifiAccessPoint(WifiAccessPoint.from(
+ mac.substring(0, mac.length() - 1), buf.readUnsignedByte()));
}
if (BitUtil.check(flags, 6)) {
- buf.skipBytes(7); // bss2
+ String mac = ByteBufUtil.hexDump(buf.readSlice(6)).replaceAll("(..)", "$1:");
+ network.addWifiAccessPoint(WifiAccessPoint.from(
+ mac.substring(0, mac.length() - 1), buf.readUnsignedByte()));
+ }
+
+ if (BitUtil.check(flags, 7)) {
+ buf.readUnsignedByte(); // radio access technology
+ int count = buf.readUnsignedByte();
+ int lac = 0;
+ if (count > 0) {
+ mcc = buf.readUnsignedShort();
+ mnc = buf.readUnsignedShort();
+ lac = buf.readUnsignedShort(); // lac
+ buf.readUnsignedShort(); // tac
+ buf.readUnsignedInt(); // cid
+ buf.readUnsignedShort(); // ta
+ }
+ for (int i = 0; i < count; i++) {
+ int cid = buf.readUnsignedShort(); // physical cid
+ buf.readUnsignedShort(); // e-arfcn
+ int rssi = buf.readUnsignedByte();
+ network.addCellTower(CellTower.from(mcc, mnc, lac, cid, rssi));
+ }
+ }
+
+ if (network.getCellTowers() != null || network.getWifiAccessPoints() != null) {
+ position.setNetwork(network);
}
if (type == MSG_WARNING) {
diff --git a/src/main/java/org/traccar/protocol/EgtsProtocol.java b/src/main/java/org/traccar/protocol/EgtsProtocol.java
index 5d4638f37..f257271d4 100644
--- a/src/main/java/org/traccar/protocol/EgtsProtocol.java
+++ b/src/main/java/org/traccar/protocol/EgtsProtocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class EgtsProtocol extends BaseProtocol {
- public EgtsProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public EgtsProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new EgtsFrameDecoder());
pipeline.addLast(new EgtsProtocolDecoder(EgtsProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/EgtsProtocolDecoder.java b/src/main/java/org/traccar/protocol/EgtsProtocolDecoder.java
index e65ddb0ef..01d329580 100644
--- a/src/main/java/org/traccar/protocol/EgtsProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/EgtsProtocolDecoder.java
@@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
@@ -291,7 +291,7 @@ public class EgtsProtocolDecoder extends BaseProtocolDecoder {
if (serviceType == SERVICE_TELEDATA && position.getValid()) {
if (useObjectIdAsDeviceId && objectId != 0L) {
- deviceSession = getDeviceSession(channel, remoteAddress, true, String.valueOf(objectId));
+ deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(objectId));
if (deviceSession != null) {
position.setDeviceId(deviceSession.getDeviceId());
}
diff --git a/src/main/java/org/traccar/protocol/EnforaProtocol.java b/src/main/java/org/traccar/protocol/EnforaProtocol.java
index e462ab322..ebde56f70 100644
--- a/src/main/java/org/traccar/protocol/EnforaProtocol.java
+++ b/src/main/java/org/traccar/protocol/EnforaProtocol.java
@@ -19,26 +19,30 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class EnforaProtocol extends BaseProtocol {
- public EnforaProtocol() {
+ @Inject
+ public EnforaProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_CUSTOM,
Command.TYPE_ENGINE_STOP,
Command.TYPE_ENGINE_RESUME);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 0, 2, -2, 2));
pipeline.addLast(new EnforaProtocolEncoder(EnforaProtocol.this));
pipeline.addLast(new EnforaProtocolDecoder(EnforaProtocol.this));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new EnforaProtocolEncoder(EnforaProtocol.this));
pipeline.addLast(new EnforaProtocolDecoder(EnforaProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/EnforaProtocolDecoder.java b/src/main/java/org/traccar/protocol/EnforaProtocolDecoder.java
index bfa7a116b..dd1c8017b 100644
--- a/src/main/java/org/traccar/protocol/EnforaProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/EnforaProtocolDecoder.java
@@ -18,7 +18,7 @@ package org.traccar.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BufferUtil;
import org.traccar.helper.DateBuilder;
diff --git a/src/main/java/org/traccar/protocol/EnnfuProtocol.java b/src/main/java/org/traccar/protocol/EnnfuProtocol.java
index 7ef94d83f..e326481fa 100644
--- a/src/main/java/org/traccar/protocol/EnnfuProtocol.java
+++ b/src/main/java/org/traccar/protocol/EnnfuProtocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class EnnfuProtocol extends BaseProtocol {
- public EnnfuProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public EnnfuProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '$'));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/EnnfuProtocolDecoder.java b/src/main/java/org/traccar/protocol/EnnfuProtocolDecoder.java
index 792ed1098..2198938e2 100644
--- a/src/main/java/org/traccar/protocol/EnnfuProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/EnnfuProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/EnvotechProtocol.java b/src/main/java/org/traccar/protocol/EnvotechProtocol.java
new file mode 100644
index 000000000..dffa1c991
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/EnvotechProtocol.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.CharacterDelimiterFrameDecoder;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
+
+public class EnvotechProtocol extends BaseProtocol {
+
+ @Inject
+ public EnvotechProtocol(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 EnvotechProtocolDecoder(EnvotechProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/EnvotechProtocolDecoder.java b/src/main/java/org/traccar/protocol/EnvotechProtocolDecoder.java
new file mode 100644
index 000000000..750ff2bda
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/EnvotechProtocolDecoder.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.session.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.regex.Pattern;
+
+public class EnvotechProtocolDecoder extends BaseProtocolDecoder {
+
+ public EnvotechProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("$")
+ .number("dd") // mode
+ .expression("...,") // hardware
+ .number("(x+),") // event
+ .number("x+,") // group
+ .number("(x+),") // device id
+ .number("(dd)(dd)(dd)") // date (ddmmyy)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("xx") // connection status
+ .number("(dd)") // rssi
+ .number("d{5},") // mcc
+ .number("(ddd)") // power
+ .number("(ddd),") // battery
+ .number("(xx)") // inputs
+ .number("(xx),") // outputs
+ .number("(xxx)?") // fuel
+ .number("(xxx)?,") // weight
+ .number("(x{8}),") // status
+ .expression("[^']*'")
+ .number("(dd)(dd)(dd)") // date (ddmmyy)
+ .number("(dd)(dd)(dd)") // time (hhmmss)
+ .number("(d)") // fix
+ .number("(d+)(d{5})([NS])") // latitude
+ .number("(d+)(d{5})([EW])") // longitude
+ .number("(ddd)") // speed
+ .number("(ddd)") // course
+ .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;
+ }
+
+ Position position = new Position(getProtocolName());
+
+ int event = parser.nextHexInt();
+ switch (event) {
+ case 0x60:
+ position.set(Position.KEY_ALARM, Position.ALARM_LOCK);
+ break;
+ case 0x61:
+ position.set(Position.KEY_ALARM, Position.ALARM_UNLOCK);
+ break;
+ default:
+ break;
+ }
+ position.set(Position.KEY_EVENT, event);
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setDeviceTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+
+ position.set(Position.KEY_RSSI, parser.nextInt());
+ position.set(Position.KEY_POWER, parser.nextInt() * 0.01);
+ position.set(Position.KEY_BATTERY, parser.nextInt() * 0.01);
+ position.set(Position.KEY_INPUT, parser.nextHexInt());
+ position.set(Position.PREFIX_OUT, parser.nextHexInt());
+ position.set(Position.KEY_FUEL_LEVEL, parser.nextHexInt());
+ position.set("weight", parser.nextHexInt());
+ position.set(Position.KEY_STATUS, parser.nextHexLong());
+
+ position.setFixTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+ position.setValid(parser.nextInt() > 0);
+ position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_DEG_HEM));
+ position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_DEG_HEM));
+ position.setSpeed(parser.nextInt());
+ position.setCourse(parser.nextInt());
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/EsealProtocol.java b/src/main/java/org/traccar/protocol/EsealProtocol.java
index fc1d342e1..0ed80dc6f 100644
--- a/src/main/java/org/traccar/protocol/EsealProtocol.java
+++ b/src/main/java/org/traccar/protocol/EsealProtocol.java
@@ -21,18 +21,22 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class EsealProtocol extends BaseProtocol {
- public EsealProtocol() {
+ @Inject
+ public EsealProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_CUSTOM,
Command.TYPE_ALARM_ARM,
Command.TYPE_ALARM_DISARM);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/EsealProtocolDecoder.java b/src/main/java/org/traccar/protocol/EsealProtocolDecoder.java
index 0a12f781d..dd15c4276 100644
--- a/src/main/java/org/traccar/protocol/EsealProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/EsealProtocolDecoder.java
@@ -17,8 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.config.Keys;
@@ -32,11 +31,15 @@ import java.util.regex.Pattern;
public class EsealProtocolDecoder extends BaseProtocolDecoder {
- private final String config;
+ private String config;
public EsealProtocolDecoder(Protocol protocol) {
super(protocol);
- config = Context.getConfig().getString(Keys.PROTOCOL_CONFIG.withPrefix(getProtocolName()));
+ }
+
+ @Override
+ protected void init() {
+ config = getConfig().getString(Keys.PROTOCOL_CONFIG.withPrefix(getProtocolName()));
}
private static final Pattern PATTERN = new PatternBuilder()
diff --git a/src/main/java/org/traccar/protocol/EskyProtocol.java b/src/main/java/org/traccar/protocol/EskyProtocol.java
index fb047c207..cb2f59dc8 100644
--- a/src/main/java/org/traccar/protocol/EskyProtocol.java
+++ b/src/main/java/org/traccar/protocol/EskyProtocol.java
@@ -20,22 +20,26 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class EskyProtocol extends BaseProtocol {
- public EskyProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public EskyProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new EskyFrameDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new EskyProtocolDecoder(EskyProtocol.this));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new EskyProtocolDecoder(EskyProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/EskyProtocolDecoder.java b/src/main/java/org/traccar/protocol/EskyProtocolDecoder.java
index 14b4376d5..4239022d0 100644
--- a/src/main/java/org/traccar/protocol/EskyProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/EskyProtocolDecoder.java
@@ -18,7 +18,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import io.netty.channel.socket.DatagramChannel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/ExtremTracProtocol.java b/src/main/java/org/traccar/protocol/ExtremTracProtocol.java
index 692fd4e99..ffc941b69 100644
--- a/src/main/java/org/traccar/protocol/ExtremTracProtocol.java
+++ b/src/main/java/org/traccar/protocol/ExtremTracProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class ExtremTracProtocol extends BaseProtocol {
- public ExtremTracProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public ExtremTracProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/ExtremTracProtocolDecoder.java b/src/main/java/org/traccar/protocol/ExtremTracProtocolDecoder.java
index 9fde6f0a0..706c70825 100644
--- a/src/main/java/org/traccar/protocol/ExtremTracProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ExtremTracProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/FifotrackProtocol.java b/src/main/java/org/traccar/protocol/FifotrackProtocol.java
index 4a0a12ed3..fd2beaabb 100644
--- a/src/main/java/org/traccar/protocol/FifotrackProtocol.java
+++ b/src/main/java/org/traccar/protocol/FifotrackProtocol.java
@@ -19,17 +19,21 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class FifotrackProtocol extends BaseProtocol {
- public FifotrackProtocol() {
+ @Inject
+ public FifotrackProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_CUSTOM,
Command.TYPE_REQUEST_PHOTO);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new FifotrackFrameDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new FifotrackProtocolEncoder(FifotrackProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
index 5f9326a61..a9d77b46e 100644
--- a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
@@ -19,8 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
@@ -35,6 +34,10 @@ import org.traccar.model.WifiAccessPoint;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
import java.util.regex.Pattern;
public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
@@ -79,7 +82,7 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
.text("$$")
.number("d+,") // length
.number("(d+),") // imei
- .number("x+,") // index
+ .number("(x+),") // index
.text("A03,") // type
.number("(d+)?,") // alarm
.number("(dd)(dd)(dd)") // date (yymmdd)
@@ -138,16 +141,20 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
.number("xx")
.compile();
- private void requestPhoto(Channel channel, SocketAddress socketAddress, String imei, String file) {
+ private void sendResponse(Channel channel, SocketAddress remoteAddress, String imei, String content) {
if (channel != null) {
- String content = "1,D06," + file + "," + photo.writerIndex() + "," + Math.min(1024, photo.writableBytes());
int length = 1 + imei.length() + 1 + content.length();
String response = String.format("##%02d,%s,%s*", length, imei, content);
response += Checksum.sum(response) + "\r\n";
- channel.writeAndFlush(new NetworkMessage(response, socketAddress));
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
}
+ private void requestPhoto(Channel channel, SocketAddress remoteAddress, String imei, String file) {
+ String content = "1,D06," + file + "," + photo.writerIndex() + "," + Math.min(1024, photo.writableBytes());
+ sendResponse(channel, remoteAddress, imei, content);
+ }
+
private String decodeAlarm(Integer alarm) {
if (alarm != null) {
switch (alarm) {
@@ -201,11 +208,14 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ String imei = parser.next();
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
if (deviceSession == null) {
return null;
}
+ String index = parser.next();
+
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
@@ -244,6 +254,11 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
position.setNetwork(network);
+ DateFormat dateFormat = new SimpleDateFormat("yyMMddHHmmss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ String response = index + ",A03," + dateFormat.format(new Date());
+ sendResponse(channel, remoteAddress, imei, response);
+
return position;
}
@@ -381,7 +396,7 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
Position position = new Position(getProtocolName());
position.setDeviceId(getDeviceSession(channel, remoteAddress, imei).getDeviceId());
getLastLocation(position, null);
- position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(imei, photo, "jpg"));
+ position.set(Position.KEY_IMAGE, writeMediaFile(imei, photo, "jpg"));
photo.release();
photo = null;
return position;
diff --git a/src/main/java/org/traccar/protocol/FlespiProtocol.java b/src/main/java/org/traccar/protocol/FlespiProtocol.java
index 05b105f93..374cf77e2 100644
--- a/src/main/java/org/traccar/protocol/FlespiProtocol.java
+++ b/src/main/java/org/traccar/protocol/FlespiProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class FlespiProtocol extends BaseProtocol {
- public FlespiProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public FlespiProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new HttpResponseEncoder());
pipeline.addLast(new HttpRequestDecoder(4096, 8192, 128 * 1024));
pipeline.addLast(new HttpObjectAggregator(Integer.MAX_VALUE));
diff --git a/src/main/java/org/traccar/protocol/FlespiProtocolDecoder.java b/src/main/java/org/traccar/protocol/FlespiProtocolDecoder.java
index 83ca74ce5..6e6f9c700 100644
--- a/src/main/java/org/traccar/protocol/FlespiProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FlespiProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,7 +19,7 @@ import io.netty.channel.Channel;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import org.traccar.BaseHttpProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
@@ -54,11 +54,11 @@ public class FlespiProtocolDecoder extends BaseHttpProtocolDecoder {
List<Position> positions = new LinkedList<>();
for (int i = 0; i < result.size(); i++) {
JsonObject message = result.getJsonObject(i);
- JsonString ident = message.getJsonString("ident");
- if (ident == null) {
+ JsonString identifier = message.getJsonString("ident");
+ if (identifier == null) {
continue;
}
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, ident.getString());
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, identifier.getString());
if (deviceSession == null) {
continue;
}
@@ -228,6 +228,15 @@ public class FlespiProtocolDecoder extends BaseHttpProtocolDecoder {
position.set(Position.KEY_ALARM, Position.ALARM_BONNET);
}
return true;
+ case "custom.wln_accel_max":
+ position.set("maxAcceleration", ((JsonNumber) value).doubleValue());
+ return true;
+ case "custom.wln_brk_max":
+ position.set("maxBraking", ((JsonNumber) value).doubleValue());
+ return true;
+ case "custom.wln_crn_max":
+ position.set("maxCornering", ((JsonNumber) value).doubleValue());
+ return true;
default:
return false;
}
diff --git a/src/main/java/org/traccar/protocol/FlexApiProtocol.java b/src/main/java/org/traccar/protocol/FlexApiProtocol.java
new file mode 100644
index 000000000..088072d2d
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/FlexApiProtocol.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2021 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.LineBasedFrameDecoder;
+import io.netty.handler.codec.string.StringDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import java.nio.charset.StandardCharsets;
+
+import javax.inject.Inject;
+
+public class FlexApiProtocol extends BaseProtocol {
+
+ @Inject
+ public FlexApiProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
+ pipeline.addLast(new LineBasedFrameDecoder(5120));
+ pipeline.addLast(new StringDecoder(StandardCharsets.US_ASCII));
+ pipeline.addLast(new FlexApiProtocolDecoder(FlexApiProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/FlexApiProtocolDecoder.java b/src/main/java/org/traccar/protocol/FlexApiProtocolDecoder.java
new file mode 100644
index 000000000..2dec44e64
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/FlexApiProtocolDecoder.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.session.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+
+import javax.json.Json;
+import javax.json.JsonObject;
+import java.io.StringReader;
+import java.net.SocketAddress;
+import java.util.Date;
+
+public class FlexApiProtocolDecoder extends BaseProtocolDecoder {
+
+ public FlexApiProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String message = (String) msg;
+ JsonObject root = Json.createReader(new StringReader(message.substring(1, message.length() - 2))).readObject();
+
+ String topic = root.getString("topic");
+ String clientId = topic.substring(3, topic.indexOf('/', 3));
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, clientId);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ JsonObject payload = root.getJsonObject("payload");
+
+ if (topic.contains("/gnss/")) {
+
+ position.setValid(true);
+
+ if (payload.containsKey("time")) {
+ position.setTime(new Date(payload.getInt("time") * 1000L));
+ position.setLatitude(payload.getJsonNumber("lat").doubleValue());
+ position.setLongitude(payload.getJsonNumber("log").doubleValue());
+ } else {
+ position.setTime(new Date(payload.getInt("gnss.ts") * 1000L));
+ position.setLatitude(payload.getJsonNumber("gnss.latitude").doubleValue());
+ position.setLongitude(payload.getJsonNumber("gnss.longitude").doubleValue());
+ }
+
+ position.setValid(payload.getInt("gnss.fix") > 0);
+ position.setAltitude(payload.getJsonNumber("gnss.altitude").doubleValue());
+ position.setSpeed(payload.getJsonNumber("gnss.speed").doubleValue());
+ position.setCourse(payload.getJsonNumber("gnss.heading").doubleValue());
+
+ position.set(Position.KEY_SATELLITES, payload.getInt("gnss.num_sv"));
+ position.set(Position.KEY_HDOP, payload.getJsonNumber("gnss.hdop").doubleValue());
+
+ } else if (topic.contains("/cellular1/")) {
+
+ getLastLocation(position, new Date(payload.getInt("modem1.ts") * 1000L));
+
+ position.set("imei", payload.getString("modem1.imei"));
+ position.set("imsi", payload.getString("modem1.imsi"));
+ position.set(Position.KEY_ICCID, payload.getString("modem1.iccid"));
+
+ String operator = payload.getString("modem1.operator");
+ if (!operator.isEmpty()) {
+ CellTower cellTower = CellTower.from(
+ Integer.parseInt(operator.substring(0, 3)),
+ Integer.parseInt(operator.substring(3)),
+ Integer.parseInt(payload.getString("modem1.lac"), 16),
+ Integer.parseInt(payload.getString("modem1.cell_id"), 16),
+ payload.getInt("modem1.rssi"));
+ switch (payload.getInt("modem1.network")) {
+ case 1:
+ cellTower.setRadioType("gsm");
+ break;
+ case 2:
+ cellTower.setRadioType("wcdma");
+ break;
+ case 3:
+ cellTower.setRadioType("lte");
+ break;
+ default:
+ break;
+ }
+ position.setNetwork(new Network(cellTower));
+ }
+
+ } else if (topic.contains("/obd/")) {
+
+ getLastLocation(position, new Date(payload.getInt("obd.ts") * 1000L));
+
+ if (payload.containsKey("obd.speed")) {
+ position.set(Position.KEY_OBD_SPEED, payload.getJsonNumber("obd.speed").doubleValue());
+ }
+ if (payload.containsKey("obd.odo")) {
+ position.set(Position.KEY_OBD_ODOMETER, payload.getInt("obd.odo"));
+ }
+ if (payload.containsKey("obd.rpm")) {
+ position.set(Position.KEY_RPM, payload.getInt("obd.rpm"));
+ }
+ if (payload.containsKey("obd.vin")) {
+ position.set(Position.KEY_VIN, payload.getString("obd.vin"));
+ }
+
+ } else if (topic.contains("/motion/")) {
+
+ getLastLocation(position, new Date(payload.getInt("motion.ts") * 1000L));
+
+ position.set("ax", payload.getJsonNumber("motion.ax").doubleValue());
+ position.set("ay", payload.getJsonNumber("motion.ay").doubleValue());
+ position.set("az", payload.getJsonNumber("motion.az").doubleValue());
+ position.set("gx", payload.getJsonNumber("motion.gx").doubleValue());
+ position.set("gy", payload.getJsonNumber("motion.gy").doubleValue());
+ position.set("gz", payload.getJsonNumber("motion.gz").doubleValue());
+
+ } else if (topic.contains("/io/")) {
+
+ getLastLocation(position, new Date(payload.getInt("io.ts") * 1000L));
+
+ if (payload.containsKey("io.IGT")) {
+ position.set(Position.KEY_IGNITION, payload.getInt("io.IGT") > 0);
+ }
+
+ for (String key : payload.keySet()) {
+ if (key.startsWith("io.AI")) {
+ position.set(Position.PREFIX_ADC + key.substring(5), payload.getJsonNumber(key).doubleValue());
+ } else if (key.startsWith("io.DI") && !key.endsWith("_pullup")) {
+ position.set(Position.PREFIX_IN + key.substring(5), payload.getInt(key) > 0);
+ } else if (key.startsWith("io.DO")) {
+ position.set(Position.PREFIX_OUT + key.substring(5), payload.getInt(key) > 0);
+ }
+ }
+
+ } else if (topic.contains("/sysinfo/")) {
+
+ getLastLocation(position, new Date(payload.getInt("sysinfo.ts") * 1000L));
+
+ position.set("serial", payload.getString("sysinfo.serial_number"));
+ position.set(Position.KEY_VERSION_FW, payload.getString("sysinfo.firmware_version"));
+
+ } else {
+
+ return null;
+
+ }
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/FlexCommProtocol.java b/src/main/java/org/traccar/protocol/FlexCommProtocol.java
index 9343ebeb8..5397156cb 100644
--- a/src/main/java/org/traccar/protocol/FlexCommProtocol.java
+++ b/src/main/java/org/traccar/protocol/FlexCommProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class FlexCommProtocol extends BaseProtocol {
- public FlexCommProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public FlexCommProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new FixedLengthFrameDecoder(2 + 2 + 101 + 5));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/FlexCommProtocolDecoder.java b/src/main/java/org/traccar/protocol/FlexCommProtocolDecoder.java
index 068c0a05c..0d8bd9373 100644
--- a/src/main/java/org/traccar/protocol/FlexCommProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FlexCommProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/FlexibleReportProtocol.java b/src/main/java/org/traccar/protocol/FlexibleReportProtocol.java
index 0cd55343a..61e315af9 100644
--- a/src/main/java/org/traccar/protocol/FlexibleReportProtocol.java
+++ b/src/main/java/org/traccar/protocol/FlexibleReportProtocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class FlexibleReportProtocol extends BaseProtocol {
- public FlexibleReportProtocol() {
- addServer(new TrackerServer(true, getName()) {
+ @Inject
+ public FlexibleReportProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new FlexibleReportProtocolDecoder(FlexibleReportProtocol.this));
}
});
diff --git a/src/main/java/org/traccar/protocol/FlexibleReportProtocolDecoder.java b/src/main/java/org/traccar/protocol/FlexibleReportProtocolDecoder.java
index 759f2cd6f..9fcee1aeb 100644
--- a/src/main/java/org/traccar/protocol/FlexibleReportProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FlexibleReportProtocolDecoder.java
@@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/FlextrackProtocol.java b/src/main/java/org/traccar/protocol/FlextrackProtocol.java
index ddd1d58f0..ebac8b4de 100644
--- a/src/main/java/org/traccar/protocol/FlextrackProtocol.java
+++ b/src/main/java/org/traccar/protocol/FlextrackProtocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class FlextrackProtocol extends BaseProtocol {
- public FlextrackProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public FlextrackProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "\r"));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/FlextrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/FlextrackProtocolDecoder.java
index 9dce22ede..a0dac1c41 100644
--- a/src/main/java/org/traccar/protocol/FlextrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FlextrackProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/FoxProtocol.java b/src/main/java/org/traccar/protocol/FoxProtocol.java
index 9bac773b5..fa45b3817 100644
--- a/src/main/java/org/traccar/protocol/FoxProtocol.java
+++ b/src/main/java/org/traccar/protocol/FoxProtocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class FoxProtocol extends BaseProtocol {
- public FoxProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public FoxProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "</fox>"));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/FoxProtocolDecoder.java b/src/main/java/org/traccar/protocol/FoxProtocolDecoder.java
index 449f00022..6dd0b0e95 100644
--- a/src/main/java/org/traccar/protocol/FoxProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FoxProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/FreedomProtocol.java b/src/main/java/org/traccar/protocol/FreedomProtocol.java
index bc6b92d5f..dac117c04 100644
--- a/src/main/java/org/traccar/protocol/FreedomProtocol.java
+++ b/src/main/java/org/traccar/protocol/FreedomProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class FreedomProtocol extends BaseProtocol {
- public FreedomProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public FreedomProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/FreedomProtocolDecoder.java b/src/main/java/org/traccar/protocol/FreedomProtocolDecoder.java
index 1d2dd3133..27dda1a6d 100644
--- a/src/main/java/org/traccar/protocol/FreedomProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FreedomProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/FreematicsProtocol.java b/src/main/java/org/traccar/protocol/FreematicsProtocol.java
index 999b075a1..dce4994ab 100644
--- a/src/main/java/org/traccar/protocol/FreematicsProtocol.java
+++ b/src/main/java/org/traccar/protocol/FreematicsProtocol.java
@@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class FreematicsProtocol extends BaseProtocol {
- public FreematicsProtocol() {
- addServer(new TrackerServer(true, getName()) {
+ @Inject
+ public FreematicsProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new FreematicsProtocolDecoder(FreematicsProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/FreematicsProtocolDecoder.java b/src/main/java/org/traccar/protocol/FreematicsProtocolDecoder.java
index aded35823..4e5200f37 100644
--- a/src/main/java/org/traccar/protocol/FreematicsProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FreematicsProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Checksum;
diff --git a/src/main/java/org/traccar/protocol/FutureWayProtocol.java b/src/main/java/org/traccar/protocol/FutureWayProtocol.java
index 73b53ee12..715dd3c9c 100644
--- a/src/main/java/org/traccar/protocol/FutureWayProtocol.java
+++ b/src/main/java/org/traccar/protocol/FutureWayProtocol.java
@@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class FutureWayProtocol extends BaseProtocol {
- public FutureWayProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public FutureWayProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new FutureWayFrameDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/FutureWayProtocolDecoder.java b/src/main/java/org/traccar/protocol/FutureWayProtocolDecoder.java
index c2f3781d9..57027b080 100644
--- a/src/main/java/org/traccar/protocol/FutureWayProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FutureWayProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.DataConverter;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/G1rusFrameDecoder.java b/src/main/java/org/traccar/protocol/G1rusFrameDecoder.java
new file mode 100644
index 000000000..8c67207ad
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/G1rusFrameDecoder.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
+
+public class G1rusFrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ ByteBuf result = Unpooled.buffer();
+
+ while (buf.isReadable()) {
+ int b = buf.readUnsignedByte();
+ if (b == 0x1B) {
+ int ext = buf.readUnsignedByte();
+ if (ext == 0x00) {
+ result.writeByte(0x1B);
+ } else {
+ result.writeByte(0xF8);
+ }
+ } else {
+ result.writeByte(b);
+ }
+ }
+
+ return result;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/G1rusProtocol.java b/src/main/java/org/traccar/protocol/G1rusProtocol.java
new file mode 100644
index 000000000..f1823762d
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/G1rusProtocol.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
+
+public class G1rusProtocol extends BaseProtocol {
+
+ @Inject
+ public G1rusProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
+ pipeline.addLast(new G1rusFrameDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new G1rusProtocolDecoder(G1rusProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/G1rusProtocolDecoder.java b/src/main/java/org/traccar/protocol/G1rusProtocolDecoder.java
new file mode 100644
index 000000000..17cfbc1eb
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/G1rusProtocolDecoder.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.model.Position;
+import org.traccar.session.DeviceSession;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+public class G1rusProtocolDecoder extends BaseProtocolDecoder {
+ public G1rusProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_HEARTBEAT = 0;
+ public static final int MSG_REGULAR = 1;
+ public static final int MSG_SMS_FORWARD = 2;
+ public static final int MSG_SERIAL = 3;
+ public static final int MSG_MIXED = 4;
+
+ private String readString(ByteBuf buf) {
+ int length = buf.readUnsignedByte() & 0xF;
+ return buf.readCharSequence(length, StandardCharsets.US_ASCII).toString();
+ }
+
+ private Position decodeRegular(DeviceSession deviceSession, ByteBuf buf, int type) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ position.setTime(new Date((buf.readUnsignedIntLE() + 946684800) * 1000L));
+
+ if (BitUtil.check(type, 6)) {
+ position.set(Position.KEY_EVENT, buf.readUnsignedByte());
+ }
+
+ int dataMask = buf.readUnsignedShort();
+
+ if (BitUtil.check(dataMask, 0)) {
+ buf.readUnsignedByte(); // length
+ readString(buf); // device name
+ position.set(Position.KEY_VERSION_FW, readString(buf));
+ position.set(Position.KEY_VERSION_HW, readString(buf));
+ }
+
+ if (BitUtil.check(dataMask, 1)) {
+ buf.readUnsignedByte(); // length
+ int locationMask = buf.readUnsignedShort();
+ if (BitUtil.check(locationMask, 0)) {
+ int validity = buf.readUnsignedByte();
+ position.set(Position.KEY_SATELLITES, BitUtil.to(validity, 5));
+ position.setValid(BitUtil.between(validity, 5, 7) == 2);
+ }
+ if (BitUtil.check(locationMask, 1)) {
+ position.setLatitude(buf.readInt() / 1000000.0);
+ position.setLongitude(buf.readInt() / 1000000.0);
+ }
+ if (BitUtil.check(locationMask, 2)) {
+ position.setSpeed(buf.readUnsignedShort());
+ }
+ if (BitUtil.check(locationMask, 3)) {
+ position.setCourse(buf.readUnsignedShort());
+ }
+ if (BitUtil.check(locationMask, 4)) {
+ position.setAltitude(buf.readShort());
+ }
+ if (BitUtil.check(locationMask, 5)) {
+ position.set(Position.KEY_HDOP, buf.readUnsignedShort());
+ }
+ if (BitUtil.check(locationMask, 6)) {
+ position.set(Position.KEY_VDOP, buf.readUnsignedShort());
+ }
+ }
+
+ if (BitUtil.check(dataMask, 2)) {
+ buf.skipBytes(buf.readUnsignedByte());
+ }
+
+ if (BitUtil.check(dataMask, 3)) {
+ buf.skipBytes(buf.readUnsignedByte());
+ }
+
+ if (BitUtil.check(dataMask, 4)) {
+ buf.readUnsignedByte(); // length
+ position.set(Position.KEY_POWER, buf.readUnsignedShort() * 110 / 4096 - 10);
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 110 / 4096 - 10);
+ position.set(Position.KEY_DEVICE_TEMP, buf.readUnsignedShort() * 110 / 4096 - 10);
+ }
+
+ if (BitUtil.check(dataMask, 5)) {
+ buf.skipBytes(buf.readUnsignedByte());
+ }
+
+ if (BitUtil.check(dataMask, 7)) {
+ buf.skipBytes(buf.readUnsignedByte());
+ }
+
+ return position;
+ }
+
+ @Override
+ protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.readUnsignedByte(); // header
+ buf.readUnsignedByte(); // version
+
+ int type = buf.readUnsignedByte();
+ String imei = String.valueOf(buf.readLong());
+ buf.readerIndex(buf.readerIndex() - 1);
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ if (BitUtil.to(type, 6) == MSG_REGULAR) {
+
+ return decodeRegular(deviceSession, buf, type);
+
+ } else if (BitUtil.to(type, 6) == MSG_MIXED) {
+
+ List<Position> positions = new LinkedList<>();
+ while (buf.readableBytes() > 5) {
+ int length = buf.readUnsignedShort();
+ int subtype = buf.readUnsignedByte();
+ if (BitUtil.to(subtype, 6) == MSG_REGULAR) {
+ positions.add(decodeRegular(deviceSession, buf, subtype));
+ } else {
+ buf.skipBytes(length - 1);
+ }
+ }
+ return positions.isEmpty() ? null : positions;
+
+ }
+
+ buf.readUnsignedShort(); // checksum
+ buf.readUnsignedByte(); // tail
+
+ return null;
+
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/GalileoProtocol.java b/src/main/java/org/traccar/protocol/GalileoProtocol.java
index a1570c9b0..90e95574a 100644
--- a/src/main/java/org/traccar/protocol/GalileoProtocol.java
+++ b/src/main/java/org/traccar/protocol/GalileoProtocol.java
@@ -18,17 +18,21 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class GalileoProtocol extends BaseProtocol {
- public GalileoProtocol() {
+ @Inject
+ public GalileoProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_CUSTOM,
Command.TYPE_OUTPUT_CONTROL);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new GalileoFrameDecoder());
pipeline.addLast(new GalileoProtocolEncoder(GalileoProtocol.this));
pipeline.addLast(new GalileoProtocolDecoder(GalileoProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java b/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java
index f29fb9850..fc8a49cf5 100644
--- a/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,15 +20,16 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
+import org.traccar.helper.BitBuffer;
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.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
@@ -36,6 +37,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.TimeZone;
public class GalileoProtocolDecoder extends BaseProtocolDecoder {
@@ -215,7 +217,6 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder {
break;
case 0xea:
position.set("userDataArray", ByteBufUtil.hexDump(buf.readSlice(buf.readUnsignedByte())));
- position.set("userDataArray", ByteBufUtil.hexDump(buf.readSlice(buf.readUnsignedByte())));
break;
default:
buf.skipBytes(getTagLength(tag));
@@ -231,7 +232,11 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder {
int header = buf.readUnsignedByte();
if (header == 0x01) {
- return decodePositions(channel, remoteAddress, buf);
+ if (buf.getUnsignedMedium(buf.readerIndex() + 2) == 0x01001c) {
+ return decodeIridiumPosition(channel, remoteAddress, buf);
+ } else {
+ return decodePositions(channel, remoteAddress, buf);
+ }
} else if (header == 0x07) {
return decodePhoto(channel, remoteAddress, buf);
}
@@ -239,10 +244,58 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- private Object decodePositions(
- Channel channel, SocketAddress remoteAddress, ByteBuf buf) throws Exception {
+ private void decodeMinimalDataSet(Position position, ByteBuf buf) {
+ BitBuffer bits = new BitBuffer(buf.readSlice(10));
+ bits.readUnsigned(1);
+
+ Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ calendar.set(Calendar.DAY_OF_YEAR, 1);
+ calendar.set(Calendar.HOUR_OF_DAY, calendar.getActualMinimum(Calendar.HOUR_OF_DAY));
+ calendar.set(Calendar.MINUTE, calendar.getActualMinimum(Calendar.MINUTE));
+ calendar.set(Calendar.SECOND, calendar.getActualMinimum(Calendar.SECOND));
+ calendar.set(Calendar.MILLISECOND, calendar.getActualMinimum(Calendar.MILLISECOND));
+ calendar.add(Calendar.SECOND, bits.readUnsigned(25));
+ position.setTime(calendar.getTime());
+
+ position.setValid(bits.readUnsigned(1) == 0);
+ position.setLongitude(360 * bits.readUnsigned(22) / 4194304.0 - 180);
+ position.setLatitude(360 * bits.readUnsigned(21) / 2097152.0 - 90);
+ if (bits.readUnsigned(1) > 0) {
+ position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
+ }
+ }
+
+ private Position decodeIridiumPosition(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
+
+ buf.readUnsignedShortLE(); // length
+
+ buf.skipBytes(3); // identification header
+ buf.readUnsignedIntLE(); // index
+
+ DeviceSession deviceSession = getDeviceSession(
+ channel, remoteAddress, buf.readSlice(15).toString(StandardCharsets.US_ASCII));
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ buf.readUnsignedByte(); // session status
+ buf.skipBytes(4); // reserved
+ buf.readUnsignedIntLE(); // date and time
+
+ buf.skipBytes(23); // coordinates block
+
+ buf.skipBytes(3); // data tag header
+ decodeMinimalDataSet(position, buf);
+
+ return position;
+ }
+
+ private List<Position> decodePositions(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
- int length = (buf.readUnsignedShortLE() & 0x7fff) + 3;
+ int endIndex = (buf.readUnsignedShortLE() & 0x7fff) + buf.readerIndex();
List<Position> positions = new LinkedList<>();
Set<Integer> tags = new HashSet<>();
@@ -251,7 +304,7 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder {
DeviceSession deviceSession = null;
Position position = new Position(getProtocolName());
- while (buf.readerIndex() < length) {
+ while (buf.readerIndex() < endIndex) {
int tag = buf.readUnsignedByte();
if (tags.contains(tag)) {
@@ -287,7 +340,7 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder {
if (hasLocation && position.getFixTime() != null) {
positions.add(position);
- } else if (position.getAttributes().containsKey(Position.KEY_RESULT)) {
+ } else if (position.hasAttribute(Position.KEY_RESULT)) {
position.setDeviceId(deviceSession.getDeviceId());
getLastLocation(position, null);
positions.add(position);
@@ -322,14 +375,13 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder {
} else {
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
- String uniqueId = Context.getIdentityManager().getById(deviceSession.getDeviceId()).getUniqueId();
position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
getLastLocation(position, null);
- position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(uniqueId, photo, "jpg"));
+ position.set(Position.KEY_IMAGE, writeMediaFile(deviceSession.getUniqueId(), photo, "jpg"));
photo.release();
photo = null;
diff --git a/src/main/java/org/traccar/protocol/GatorProtocol.java b/src/main/java/org/traccar/protocol/GatorProtocol.java
index ca81caefb..7341b69a3 100644
--- a/src/main/java/org/traccar/protocol/GatorProtocol.java
+++ b/src/main/java/org/traccar/protocol/GatorProtocol.java
@@ -19,20 +19,24 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class GatorProtocol extends BaseProtocol {
- public GatorProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public GatorProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 3, 2));
pipeline.addLast(new GatorProtocolDecoder(GatorProtocol.this));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new GatorProtocolDecoder(GatorProtocol.this));
}
});
diff --git a/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java b/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java
index 087861635..644caee81 100644
--- a/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BcdUtil;
diff --git a/src/main/java/org/traccar/protocol/GenxProtocol.java b/src/main/java/org/traccar/protocol/GenxProtocol.java
index c87ba946a..97d8633a0 100644
--- a/src/main/java/org/traccar/protocol/GenxProtocol.java
+++ b/src/main/java/org/traccar/protocol/GenxProtocol.java
@@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class GenxProtocol extends BaseProtocol {
- public GenxProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public GenxProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new GenxProtocolDecoder(GenxProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/GenxProtocolDecoder.java b/src/main/java/org/traccar/protocol/GenxProtocolDecoder.java
index 2ae9de7a0..6448b6a5a 100644
--- a/src/main/java/org/traccar/protocol/GenxProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GenxProtocolDecoder.java
@@ -17,8 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
@@ -32,7 +31,11 @@ public class GenxProtocolDecoder extends BaseProtocolDecoder {
public GenxProtocolDecoder(Protocol protocol) {
super(protocol);
- setReportColumns(Context.getConfig().getString(getProtocolName() + ".reportColumns", "1,2,3,4"));
+ }
+
+ @Override
+ protected void init() {
+ setReportColumns(getConfig().getString(getProtocolName() + ".reportColumns", "1,2,3,4"));
}
public void setReportColumns(String format) {
diff --git a/src/main/java/org/traccar/protocol/Gl100Protocol.java b/src/main/java/org/traccar/protocol/Gl100Protocol.java
index 063e606db..e1748c9a0 100644
--- a/src/main/java/org/traccar/protocol/Gl100Protocol.java
+++ b/src/main/java/org/traccar/protocol/Gl100Protocol.java
@@ -21,22 +21,26 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class Gl100Protocol extends BaseProtocol {
- public Gl100Protocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public Gl100Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '\0'));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new Gl100ProtocolDecoder(Gl100Protocol.this));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new Gl100ProtocolDecoder(Gl100Protocol.this));
diff --git a/src/main/java/org/traccar/protocol/Gl100ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl100ProtocolDecoder.java
index ae0383e5c..789d87dad 100644
--- a/src/main/java/org/traccar/protocol/Gl100ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gl100ProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/Gl200BinaryProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200BinaryProtocolDecoder.java
index c3339bea5..ecd1f5bfa 100644
--- a/src/main/java/org/traccar/protocol/Gl200BinaryProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gl200BinaryProtocolDecoder.java
@@ -18,7 +18,7 @@ package org.traccar.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BitBuffer;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/Gl200Protocol.java b/src/main/java/org/traccar/protocol/Gl200Protocol.java
index e2d0c6d2a..c7b6a8e7c 100644
--- a/src/main/java/org/traccar/protocol/Gl200Protocol.java
+++ b/src/main/java/org/traccar/protocol/Gl200Protocol.java
@@ -15,34 +15,37 @@
*/
package org.traccar.protocol;
+import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
-import io.netty.handler.codec.string.StringEncoder;
+import javax.inject.Inject;
public class Gl200Protocol extends BaseProtocol {
- public Gl200Protocol() {
+ @Inject
+ public Gl200Protocol(Config config) {
setSupportedDataCommands(
Command.TYPE_POSITION_SINGLE,
Command.TYPE_ENGINE_STOP,
Command.TYPE_ENGINE_RESUME,
Command.TYPE_IDENTIFICATION,
Command.TYPE_REBOOT_DEVICE);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new Gl200FrameDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new Gl200ProtocolEncoder(Gl200Protocol.this));
pipeline.addLast(new Gl200ProtocolDecoder(Gl200Protocol.this));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringEncoder());
pipeline.addLast(new Gl200ProtocolEncoder(Gl200Protocol.this));
pipeline.addLast(new Gl200ProtocolDecoder(Gl200Protocol.this));
diff --git a/src/main/java/org/traccar/protocol/Gl200ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200ProtocolDecoder.java
index ca1df7a13..a9736c9e7 100644
--- a/src/main/java/org/traccar/protocol/Gl200ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gl200ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,12 +15,14 @@
*/
package org.traccar.protocol;
+import com.google.inject.Injector;
import org.traccar.BaseProtocolDecoder;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import org.traccar.Protocol;
+import javax.inject.Inject;
import java.net.SocketAddress;
public class Gl200ProtocolDecoder extends BaseProtocolDecoder {
@@ -34,6 +36,12 @@ public class Gl200ProtocolDecoder extends BaseProtocolDecoder {
binaryProtocolDecoder = new Gl200BinaryProtocolDecoder(protocol);
}
+ @Inject
+ public void setInjector(Injector injector) {
+ injector.injectMembers(textProtocolDecoder);
+ injector.injectMembers(binaryProtocolDecoder);
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
index 683ba476e..517499f02 100644
--- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,8 +16,7 @@
package org.traccar.protocol;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.config.Keys;
@@ -46,11 +45,15 @@ import java.util.regex.Pattern;
public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
- private final boolean ignoreFixTime;
+ private boolean ignoreFixTime;
public Gl200TextProtocolDecoder(Protocol protocol) {
super(protocol);
- ignoreFixTime = Context.getConfig().getBoolean(Keys.PROTOCOL_IGNORE_FIX_TIME.withPrefix(getProtocolName()));
+ }
+
+ @Override
+ protected void init() {
+ ignoreFixTime = getConfig().getBoolean(Keys.PROTOCOL_IGNORE_FIX_TIME.withPrefix(getProtocolName()));
}
private static final Pattern PATTERN_ACK = new PatternBuilder()
@@ -139,7 +142,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
.number("(x+)?,") // lac
.number("(x+)?,") // cid
.groupEnd()
- .number("(?:d+|(d+.d))?,") // odometer
+ .number("(?:d+|(d+.d))?,") // rssi / odometer
.compile();
private static final Pattern PATTERN_OBD = new PatternBuilder()
@@ -184,7 +187,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
.expression("(?:([0-9A-Z]{17}),)?") // vin
.expression("[^,]*,") // device name
.number("(d+)?,") // power
- .number("d{1,2},").optional() // report type
+ .number("(d{1,2}),").optional() // report type
.number("d{1,2},").optional() // count
.number("d*,").optional() // reserved
.number("(d+),").optional() // battery
@@ -208,11 +211,11 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
.number("(?:d+.?d*|Inf|NaN)?,") // fuel consumption
.number("(d+)?,") // fuel level
.or()
- .number("(d{1,7}.d)?,").optional() // odometer
- .number("(d{1,3})?,") // battery
- .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)
@@ -349,6 +352,22 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
.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
@@ -357,15 +376,16 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
.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)?,").optional() // odometer
+ .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)
+ .number("(dd)(dd)(dd)") // time (hhmmss)
.text(",")
.number("(xxxx)") // count number
.text("$").optional()
@@ -377,6 +397,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
.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
@@ -835,6 +856,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
String vin = parser.next();
Integer power = parser.nextInt();
+ Integer reportType = parser.nextInt();
Integer battery = parser.nextInt();
Parser itemParser = new Parser(PATTERN_LOCATION, parser.next());
@@ -877,12 +899,18 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_RPM, parser.nextInt());
position.set(Position.KEY_FUEL_LEVEL, parser.nextInt());
+ if (parser.hasNext(2)) {
+ if (reportType != null) {
+ position.set(Position.KEY_MOTION, BitUtil.check(reportType, 0));
+ position.set(Position.KEY_CHARGE, BitUtil.check(reportType, 1));
+ }
+ position.set(Position.KEY_RSSI, parser.nextInt());
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
+ }
if (parser.hasNext()) {
position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
}
position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
- position.set(Position.KEY_RSSI, parser.nextInt());
- position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
decodeDeviceTime(position, parser);
if (ignoreFixTime) {
@@ -1114,6 +1142,29 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private Object decodeDar(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_DAR, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ int warningType = parser.nextInt();
+ int fatigueDegree = parser.nextInt();
+ if (warningType == 1) {
+ position.set(Position.KEY_ALARM, Position.ALARM_FATIGUE_DRIVING);
+ position.set("fatigueDegree", fatigueDegree);
+ } else {
+ position.set("warningType", warningType);
+ }
+
+ decodeLocation(position, parser);
+
+ decodeDeviceTime(position, parser);
+
+ return position;
+ }
+
private Object decodeOther(Channel channel, SocketAddress remoteAddress, String sentence, String type) {
Parser parser = new Parser(PATTERN, sentence);
Position position = initPosition(parser, channel, remoteAddress);
@@ -1305,6 +1356,9 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
case "PFA":
result = decodePna(channel, remoteAddress, sentence);
break;
+ case "DAR":
+ result = decodeDar(channel, remoteAddress, sentence);
+ break;
default:
result = decodeOther(channel, remoteAddress, sentence, type);
break;
@@ -1325,7 +1379,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
}
}
- if (channel != null && Context.getConfig().getBoolean(Keys.PROTOCOL_ACK.withPrefix(getProtocolName()))) {
+ if (channel != null && getConfig().getBoolean(Keys.PROTOCOL_ACK.withPrefix(getProtocolName()))) {
String checksum;
if (sentence.endsWith("$")) {
checksum = sentence.substring(sentence.length() - 1 - 4, sentence.length() - 1);
diff --git a/src/main/java/org/traccar/protocol/GlobalSatProtocol.java b/src/main/java/org/traccar/protocol/GlobalSatProtocol.java
index e86b5dc30..16b99f426 100644
--- a/src/main/java/org/traccar/protocol/GlobalSatProtocol.java
+++ b/src/main/java/org/traccar/protocol/GlobalSatProtocol.java
@@ -21,18 +21,22 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class GlobalSatProtocol extends BaseProtocol {
- public GlobalSatProtocol() {
+ @Inject
+ public GlobalSatProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_CUSTOM,
Command.TYPE_ALARM_DISMISS,
Command.TYPE_OUTPUT_CONTROL);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "!\r\n", "!"));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/GlobalSatProtocolDecoder.java b/src/main/java/org/traccar/protocol/GlobalSatProtocolDecoder.java
index b48df4047..720b61695 100644
--- a/src/main/java/org/traccar/protocol/GlobalSatProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GlobalSatProtocolDecoder.java
@@ -17,8 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
@@ -40,9 +39,12 @@ public class GlobalSatProtocolDecoder extends BaseProtocolDecoder {
public GlobalSatProtocolDecoder(Protocol protocol) {
super(protocol);
+ }
- format0 = Context.getConfig().getString(getProtocolName() + ".format0", "TSPRXAB27GHKLMnaicz*U!");
- format1 = Context.getConfig().getString(getProtocolName() + ".format1", "SARY*U!");
+ @Override
+ protected void init() {
+ format0 = getConfig().getString(getProtocolName() + ".format0", "TSPRXAB27GHKLMnaicz*U!");
+ format1 = getConfig().getString(getProtocolName() + ".format1", "SARY*U!");
}
public void setFormat0(String format) {
diff --git a/src/main/java/org/traccar/protocol/GlobalstarProtocol.java b/src/main/java/org/traccar/protocol/GlobalstarProtocol.java
index 84cd5565b..293f5fda5 100644
--- a/src/main/java/org/traccar/protocol/GlobalstarProtocol.java
+++ b/src/main/java/org/traccar/protocol/GlobalstarProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class GlobalstarProtocol extends BaseProtocol {
- public GlobalstarProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public GlobalstarProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new HttpResponseEncoder());
pipeline.addLast(new HttpRequestDecoder());
pipeline.addLast(new HttpObjectAggregator(65535));
diff --git a/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java b/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java
index b742d0cac..0ddb95c14 100644
--- a/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java
@@ -27,7 +27,7 @@ import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import org.traccar.BaseHttpProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
@@ -151,15 +151,11 @@ public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder {
position.setCourse(BitUtil.from(flags, 5) * 45);
- position.setLatitude(buf.readUnsignedMedium() * 90.0 / (1 << 23));
- if (position.getLatitude() > 90) {
- position.setLatitude(position.getLatitude() - 180);
- }
+ double latitude = buf.readUnsignedMedium() * 90.0 / (1 << 23);
+ position.setLatitude(latitude > 90 ? latitude - 180 : latitude);
- position.setLongitude(buf.readUnsignedMedium() * 180.0 / (1 << 23));
- if (position.getLongitude() > 180) {
- position.setLongitude(position.getLongitude() - 360);
- }
+ double longitude = buf.readUnsignedMedium() * 180.0 / (1 << 23);
+ position.setLongitude(longitude > 180 ? longitude - 360 : longitude);
int speed = buf.readUnsignedByte();
position.setSpeed(UnitsConverter.knotsFromKph(speed));
diff --git a/src/main/java/org/traccar/protocol/GnxProtocol.java b/src/main/java/org/traccar/protocol/GnxProtocol.java
index 3576bf805..32d642688 100644
--- a/src/main/java/org/traccar/protocol/GnxProtocol.java
+++ b/src/main/java/org/traccar/protocol/GnxProtocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class GnxProtocol extends BaseProtocol {
- public GnxProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public GnxProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "\n\r"));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/GnxProtocolDecoder.java b/src/main/java/org/traccar/protocol/GnxProtocolDecoder.java
index c9c221a69..9c8b6879a 100644
--- a/src/main/java/org/traccar/protocol/GnxProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GnxProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/GoSafeProtocol.java b/src/main/java/org/traccar/protocol/GoSafeProtocol.java
index aaaffac97..607931500 100644
--- a/src/main/java/org/traccar/protocol/GoSafeProtocol.java
+++ b/src/main/java/org/traccar/protocol/GoSafeProtocol.java
@@ -21,22 +21,26 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class GoSafeProtocol extends BaseProtocol {
- public GoSafeProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public GoSafeProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '#'));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new GoSafeProtocolDecoder(GoSafeProtocol.this));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new GoSafeProtocolDecoder(GoSafeProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/GoSafeProtocolDecoder.java b/src/main/java/org/traccar/protocol/GoSafeProtocolDecoder.java
index a86249224..77649a041 100644
--- a/src/main/java/org/traccar/protocol/GoSafeProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GoSafeProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/GotopProtocol.java b/src/main/java/org/traccar/protocol/GotopProtocol.java
index 07fe02248..53fcea0d0 100644
--- a/src/main/java/org/traccar/protocol/GotopProtocol.java
+++ b/src/main/java/org/traccar/protocol/GotopProtocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class GotopProtocol extends BaseProtocol {
- public GotopProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public GotopProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '#'));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/GotopProtocolDecoder.java b/src/main/java/org/traccar/protocol/GotopProtocolDecoder.java
index a867451aa..5c8d0bac2 100644
--- a/src/main/java/org/traccar/protocol/GotopProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GotopProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -35,7 +35,7 @@ public class GotopProtocolDecoder extends BaseProtocolDecoder {
private static final Pattern PATTERN = new PatternBuilder()
.number("(d+),") // imei
- .expression("[^,]+,") // type
+ .expression("([^,]+),") // type
.expression("([AV]),") // validity
.number("DATE:(dd)(dd)(dd),") // date (yyddmm)
.number("TIME:(dd)(dd)(dd),") // time (hhmmss)
@@ -56,14 +56,25 @@ public class GotopProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position(getProtocolName());
-
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
return null;
}
+
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
+ String type = parser.next();
+ if (type.equals("CMD-KEY")) {
+ position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ } else if (type.startsWith("ALM-B")) {
+ if (Character.getNumericValue(type.charAt(5)) % 2 > 0) {
+ position.set(Position.KEY_ALARM, Position.ALARM_GEOFENCE_ENTER);
+ } else {
+ position.set(Position.KEY_ALARM, Position.ALARM_GEOFENCE_EXIT);
+ }
+ }
+
position.setValid(parser.next().equals("A"));
position.setTime(parser.nextDateTime());
diff --git a/src/main/java/org/traccar/protocol/Gps056Protocol.java b/src/main/java/org/traccar/protocol/Gps056Protocol.java
index b6ab10a19..dbffbfdbb 100644
--- a/src/main/java/org/traccar/protocol/Gps056Protocol.java
+++ b/src/main/java/org/traccar/protocol/Gps056Protocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class Gps056Protocol extends BaseProtocol {
- public Gps056Protocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public Gps056Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new Gps056FrameDecoder());
pipeline.addLast(new Gps056ProtocolDecoder(Gps056Protocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/Gps056ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gps056ProtocolDecoder.java
index 0ba79bb51..eea64364e 100644
--- a/src/main/java/org/traccar/protocol/Gps056ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gps056ProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
diff --git a/src/main/java/org/traccar/protocol/Gps103Protocol.java b/src/main/java/org/traccar/protocol/Gps103Protocol.java
index 5356387ce..2725494c5 100644
--- a/src/main/java/org/traccar/protocol/Gps103Protocol.java
+++ b/src/main/java/org/traccar/protocol/Gps103Protocol.java
@@ -21,11 +21,15 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class Gps103Protocol extends BaseProtocol {
- public Gps103Protocol() {
+ @Inject
+ public Gps103Protocol(Config config) {
setSupportedDataCommands(
Command.TYPE_CUSTOM,
Command.TYPE_POSITION_SINGLE,
@@ -36,9 +40,9 @@ public class Gps103Protocol extends BaseProtocol {
Command.TYPE_ALARM_ARM,
Command.TYPE_ALARM_DISARM,
Command.TYPE_REQUEST_PHOTO);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(2048, false, "\r\n", "\n", ";", "*"));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
@@ -46,9 +50,9 @@ public class Gps103Protocol extends BaseProtocol {
pipeline.addLast(new Gps103ProtocolDecoder(Gps103Protocol.this));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new Gps103ProtocolEncoder(Gps103Protocol.this));
diff --git a/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java
index d74f19179..28efa3c30 100644
--- a/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java
@@ -19,8 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.DataConverter;
@@ -57,9 +56,12 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder {
.groupEnd()
.expression("([^,]+)?,") // rfid
.groupBegin()
- .text("L,,,")
+ .text("L,")
+ .groupBegin()
+ .text(",,")
.number("(x+),,") // lac
.number("(x+),,,") // cid
+ .groupEnd("?")
.or()
.text("F,")
.groupBegin()
@@ -203,7 +205,7 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.PREFIX_TEMP + 1, Double.parseDouble(alarm.substring(2)));
} else if (alarm.startsWith("oil ")) {
position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(alarm.substring(4)));
- } else if (!position.getAttributes().containsKey(Position.KEY_ALARM) && !alarm.equals("tracker")) {
+ } else if (!position.hasAttribute(Position.KEY_ALARM) && !alarm.equals("tracker")) {
position.set(Position.KEY_EVENT, alarm);
}
@@ -219,12 +221,11 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder {
}
if (parser.hasNext(2)) {
+ position.setNetwork(new Network(CellTower.fromLacCid(
+ getConfig(), parser.nextHexInt(0), parser.nextHexInt(0))));
+ }
- getLastLocation(position, null);
-
- position.setNetwork(new Network(CellTower.fromLacCid(parser.nextHexInt(0), parser.nextHexInt(0))));
-
- } else {
+ if (parser.hasNext(20)) {
String utcHours = parser.next();
String utcMinutes = parser.next();
@@ -262,6 +263,10 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder {
position.set("fuel2", parser.nextDouble());
position.set(Position.PREFIX_TEMP + 1, parser.nextInt());
+ } else {
+
+ getLastLocation(position, null);
+
}
return position;
@@ -361,7 +366,7 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder {
getLastLocation(position, null);
try {
- position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(imei, photo, "jpg"));
+ position.set(Position.KEY_IMAGE, writeMediaFile(imei, photo, "jpg"));
} finally {
photoPackets = 0;
photo.release();
diff --git a/src/main/java/org/traccar/protocol/Gps103ProtocolEncoder.java b/src/main/java/org/traccar/protocol/Gps103ProtocolEncoder.java
index e662e9b04..9a899eeeb 100644
--- a/src/main/java/org/traccar/protocol/Gps103ProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/Gps103ProtocolEncoder.java
@@ -49,7 +49,7 @@ public class Gps103ProtocolEncoder extends StringProtocolEncoder implements Stri
case Command.TYPE_CUSTOM:
return formatCommand(command, "**,imei:%s,%s", Command.KEY_UNIQUE_ID, Command.KEY_DATA);
case Command.TYPE_POSITION_STOP:
- return formatCommand(command, "**,imei:%s,A", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "**,imei:%s,D", Command.KEY_UNIQUE_ID);
case Command.TYPE_POSITION_SINGLE:
return formatCommand(command, "**,imei:%s,B", Command.KEY_UNIQUE_ID);
case Command.TYPE_POSITION_PERIODIC:
diff --git a/src/main/java/org/traccar/protocol/GpsGateProtocol.java b/src/main/java/org/traccar/protocol/GpsGateProtocol.java
index a131b6f48..a6a73ae6b 100644
--- a/src/main/java/org/traccar/protocol/GpsGateProtocol.java
+++ b/src/main/java/org/traccar/protocol/GpsGateProtocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class GpsGateProtocol extends BaseProtocol {
- public GpsGateProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public GpsGateProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "\0", "\n", "\r\n"));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/GpsGateProtocolDecoder.java b/src/main/java/org/traccar/protocol/GpsGateProtocolDecoder.java
index c158d3212..82da58f1e 100644
--- a/src/main/java/org/traccar/protocol/GpsGateProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GpsGateProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Checksum;
diff --git a/src/main/java/org/traccar/protocol/GpsMarkerProtocol.java b/src/main/java/org/traccar/protocol/GpsMarkerProtocol.java
index ad23ece48..12b53342c 100644
--- a/src/main/java/org/traccar/protocol/GpsMarkerProtocol.java
+++ b/src/main/java/org/traccar/protocol/GpsMarkerProtocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class GpsMarkerProtocol extends BaseProtocol {
- public GpsMarkerProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public GpsMarkerProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "\r"));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/GpsMarkerProtocolDecoder.java b/src/main/java/org/traccar/protocol/GpsMarkerProtocolDecoder.java
index bbb2c31e2..0fef4b7da 100644
--- a/src/main/java/org/traccar/protocol/GpsMarkerProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GpsMarkerProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/GpsmtaProtocol.java b/src/main/java/org/traccar/protocol/GpsmtaProtocol.java
index ce6cc5929..a474b1e53 100644
--- a/src/main/java/org/traccar/protocol/GpsmtaProtocol.java
+++ b/src/main/java/org/traccar/protocol/GpsmtaProtocol.java
@@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class GpsmtaProtocol extends BaseProtocol {
- public GpsmtaProtocol() {
- addServer(new TrackerServer(true, getName()) {
+ @Inject
+ public GpsmtaProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new GpsmtaProtocolDecoder(GpsmtaProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/GpsmtaProtocolDecoder.java b/src/main/java/org/traccar/protocol/GpsmtaProtocolDecoder.java
index 31f9401b4..a9b85d255 100644
--- a/src/main/java/org/traccar/protocol/GpsmtaProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GpsmtaProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/GranitProtocol.java b/src/main/java/org/traccar/protocol/GranitProtocol.java
index 244c3977b..bb66501e2 100644
--- a/src/main/java/org/traccar/protocol/GranitProtocol.java
+++ b/src/main/java/org/traccar/protocol/GranitProtocol.java
@@ -19,11 +19,15 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class GranitProtocol extends BaseProtocol {
- public GranitProtocol() {
+ @Inject
+ public GranitProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_IDENTIFICATION,
Command.TYPE_REBOOT_DEVICE,
@@ -32,9 +36,9 @@ public class GranitProtocol extends BaseProtocol {
setSupportedTextCommands(
Command.TYPE_REBOOT_DEVICE,
Command.TYPE_POSITION_PERIODIC);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new GranitFrameDecoder());
pipeline.addLast(new GranitProtocolEncoder(GranitProtocol.this));
pipeline.addLast(new GranitProtocolDecoder(GranitProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/GranitProtocolDecoder.java b/src/main/java/org/traccar/protocol/GranitProtocolDecoder.java
index 292e43a0e..dfc3c10f6 100644
--- a/src/main/java/org/traccar/protocol/GranitProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GranitProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/Gs100Protocol.java b/src/main/java/org/traccar/protocol/Gs100Protocol.java
index a701815d0..425ca9330 100644
--- a/src/main/java/org/traccar/protocol/Gs100Protocol.java
+++ b/src/main/java/org/traccar/protocol/Gs100Protocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class Gs100Protocol extends BaseProtocol {
- public Gs100Protocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public Gs100Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new Gs100ProtocolDecoder(Gs100Protocol.this));
}
});
diff --git a/src/main/java/org/traccar/protocol/Gs100ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gs100ProtocolDecoder.java
index 2496aad48..352070107 100644
--- a/src/main/java/org/traccar/protocol/Gs100ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gs100ProtocolDecoder.java
@@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BcdUtil;
diff --git a/src/main/java/org/traccar/protocol/Gt02Protocol.java b/src/main/java/org/traccar/protocol/Gt02Protocol.java
index f412ee720..fa05761f6 100644
--- a/src/main/java/org/traccar/protocol/Gt02Protocol.java
+++ b/src/main/java/org/traccar/protocol/Gt02Protocol.java
@@ -19,13 +19,17 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class Gt02Protocol extends BaseProtocol {
- public Gt02Protocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public Gt02Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(256, 2, 1, 2, 0));
pipeline.addLast(new Gt02ProtocolDecoder(Gt02Protocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/Gt02ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt02ProtocolDecoder.java
index 78a3fd3ee..4ecb0b43b 100644
--- a/src/main/java/org/traccar/protocol/Gt02ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gt02ProtocolDecoder.java
@@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/Gt06Protocol.java b/src/main/java/org/traccar/protocol/Gt06Protocol.java
index 9ec8de098..38278121c 100644
--- a/src/main/java/org/traccar/protocol/Gt06Protocol.java
+++ b/src/main/java/org/traccar/protocol/Gt06Protocol.java
@@ -18,18 +18,22 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class Gt06Protocol extends BaseProtocol {
- public Gt06Protocol() {
+ @Inject
+ public Gt06Protocol(Config config) {
setSupportedDataCommands(
Command.TYPE_ENGINE_STOP,
Command.TYPE_ENGINE_RESUME,
Command.TYPE_CUSTOM);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new Gt06FrameDecoder());
pipeline.addLast(new Gt06ProtocolEncoder(Gt06Protocol.this));
pipeline.addLast(new Gt06ProtocolDecoder(Gt06Protocol.this));
diff --git a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
index 0dcdab892..ef09677bf 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 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,8 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BcdUtil;
@@ -32,7 +31,6 @@ import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.CellTower;
-import org.traccar.model.Device;
import org.traccar.model.Network;
import org.traccar.model.Position;
import org.traccar.model.WifiAccessPoint;
@@ -70,19 +68,20 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_GPS_LBS_STATUS_3 = 0x27;
public static final int MSG_LBS_MULTIPLE_1 = 0x28;
public static final int MSG_LBS_MULTIPLE_2 = 0x2E;
+ public static final int MSG_LBS_MULTIPLE_3 = 0x24;
public static final int MSG_LBS_WIFI = 0x2C;
public static final int MSG_LBS_EXTEND = 0x18;
public static final int MSG_LBS_STATUS = 0x19;
public static final int MSG_GPS_PHONE = 0x1A;
- public static final int MSG_GPS_LBS_EXTEND = 0x1E;
- public static final int MSG_HEARTBEAT = 0x23;
- public static final int MSG_ADDRESS_REQUEST = 0x2A;
- public static final int MSG_ADDRESS_RESPONSE = 0x97;
- public static final int MSG_GPS_LBS_5 = 0x31;
- public static final int MSG_GPS_LBS_STATUS_4 = 0x32;
- public static final int MSG_WIFI_5 = 0x33;
- public static final int MSG_AZ735_GPS = 0x32; // only extended
- public static final int MSG_AZ735_ALARM = 0x33; // only extended
+ public static final int MSG_GPS_LBS_EXTEND = 0x1E; // JI09
+ public static final int MSG_HEARTBEAT = 0x23; // GK310
+ public static final int MSG_ADDRESS_REQUEST = 0x2A; // GK310
+ public static final int MSG_ADDRESS_RESPONSE = 0x97; // GK310
+ public static final int MSG_GPS_LBS_5 = 0x31; // AZ735
+ 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_X1_GPS = 0x34;
public static final int MSG_X1_PHOTO_INFO = 0x35;
public static final int MSG_X1_PHOTO_DATA = 0x36;
@@ -92,25 +91,63 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_COMMAND_0 = 0x80;
public static final int MSG_COMMAND_1 = 0x81;
public static final int MSG_COMMAND_2 = 0x82;
- public static final int MSG_TIME_REQUEST = 0x8A;
+ public static final int MSG_TIME_REQUEST = 0x8A; // GK310
public static final int MSG_INFO = 0x94;
public static final int MSG_SERIAL = 0x9B;
public static final int MSG_STRING_INFO = 0x21;
- public static final int MSG_GPS_2 = 0xA0;
- public static final int MSG_LBS_2 = 0xA1;
- public static final int MSG_WIFI_3 = 0xA2;
- public static final int MSG_FENCE_SINGLE = 0xA3;
- public static final int MSG_FENCE_MULTI = 0xA4;
- public static final int MSG_LBS_ALARM = 0xA5;
- public static final int MSG_LBS_ADDRESS = 0xA7;
- public static final int MSG_OBD = 0x8C;
- public static final int MSG_DTC = 0x65;
- public static final int MSG_PID = 0x66;
- public static final int MSG_BMS = 0x20;
- public static final int MSG_MULTIMEDIA = 0x21;
- public static final int MSG_BMS_2 = 0x40;
- public static final int MSG_MULTIMEDIA_2 = 0x41;
- public static final int MSG_ALARM = 0x95;
+ public static final int MSG_GPS_2 = 0xA0; // GK310
+ public static final int MSG_LBS_2 = 0xA1; // GK310
+ public static final int MSG_WIFI_3 = 0xA2; // GK310
+ public static final int MSG_FENCE_SINGLE = 0xA3; // GK310
+ public static final int MSG_FENCE_MULTI = 0xA4; // GK310
+ public static final int MSG_LBS_ALARM = 0xA5; // GK310 & JM-LL301
+ public static final int MSG_LBS_ADDRESS = 0xA7; // GK310
+ public static final int MSG_OBD = 0x8C; // FM08ABC
+ public static final int MSG_DTC = 0x65; // FM08ABC
+ public static final int MSG_PID = 0x66; // FM08ABC
+ public static final int MSG_BMS = 0x40; // WD-209
+ public static final int MSG_MULTIMEDIA = 0x41; // WD-209
+ public static final int MSG_ALARM = 0x95; // JC100
+
+ private enum Variant {
+ VXT01,
+ WANWAY_S20,
+ SR411_MINI,
+ GT06E_CARD,
+ BENWAY,
+ S5,
+ SPACE10X,
+ STANDARD,
+ OBD6,
+ }
+
+ private Variant variant;
+
+ private static final Pattern PATTERN_FUEL = new PatternBuilder()
+ .text("!AIOIL,")
+ .number("d+,") // device address
+ .number("d+.d+,") // output value
+ .number("(d+.d+),") // temperature
+ .expression("[^,]+,") // version
+ .number("dd") // back wave
+ .number("d") // software status code
+ .number("d,") // hardware status code
+ .number("(d+.d+),") // measured value
+ .expression("[01],") // movement status
+ .number("d+,") // excited wave times
+ .number("xx") // checksum
+ .compile();
+
+ private static final Pattern PATTERN_LOCATION = new PatternBuilder()
+ .text("Current position!")
+ .number("Lat:([NS])(d+.d+),") // latitude
+ .number("Lon:([EW])(d+.d+),") // longitude
+ .text("Course:").number("(d+.d+),") // course
+ .text("Speed:").number("(d+.d+),") // speed
+ .text("DateTime:")
+ .number("(dddd)-(dd)-(dd) +") // date
+ .number("(dd):(dd):(dd)") // time
+ .compile();
private static boolean isSupported(int type) {
return hasGps(type) || hasLbs(type) || hasStatus(type);
@@ -172,6 +209,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_LBS_ALARM:
return true;
default:
return false;
@@ -297,9 +335,26 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
}
int mcc = buf.readUnsignedShort();
- int mnc = BitUtil.check(mcc, 15) || type == MSG_GPS_LBS_6 ? buf.readUnsignedShort() : buf.readUnsignedByte();
- int lac = buf.readUnsignedShort();
- long cid = type == MSG_GPS_LBS_6 ? buf.readUnsignedInt() : buf.readUnsignedMedium();
+ int mnc;
+ if (BitUtil.check(mcc, 15) || type == MSG_GPS_LBS_6) {
+ mnc = buf.readUnsignedShort();
+ } else {
+ mnc = buf.readUnsignedByte();
+ }
+ int lac;
+ if (type == MSG_LBS_ALARM) {
+ lac = buf.readInt();
+ } else {
+ lac = buf.readUnsignedShort();
+ }
+ long cid;
+ if (type == MSG_LBS_ALARM) {
+ cid = buf.readLong();
+ } else if (type == MSG_GPS_LBS_6) {
+ cid = buf.readUnsignedInt();
+ } else {
+ cid = buf.readUnsignedMedium();
+ }
position.setNetwork(new Network(CellTower.from(BitUtil.to(mcc, 15), mnc, lac, cid)));
@@ -310,7 +365,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
return true;
}
- private boolean decodeStatus(Position position, ByteBuf buf, boolean batteryLevel) {
+ private void decodeStatus(Position position, ByteBuf buf) {
int status = buf.readUnsignedByte();
@@ -332,22 +387,19 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
case 4:
position.set(Position.KEY_ALARM, Position.ALARM_SOS);
break;
+ case 6:
+ position.set(Position.KEY_ALARM, Position.ALARM_GEOFENCE);
+ break;
case 7:
- position.set(Position.KEY_ALARM, Position.ALARM_REMOVING);
+ if (variant == Variant.VXT01) {
+ position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED);
+ } else {
+ position.set(Position.KEY_ALARM, Position.ALARM_REMOVING);
+ }
break;
default:
break;
}
-
- if (batteryLevel) {
- position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte() * 100 / 6);
- } else {
- position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01);
- }
- position.set(Position.KEY_RSSI, buf.readUnsignedByte());
- position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
-
- return true;
}
private String decodeAlarm(short value) {
@@ -367,13 +419,20 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
return Position.ALARM_OVERSPEED;
case 0x0E:
case 0x0F:
+ case 0x19:
return Position.ALARM_LOW_BATTERY;
case 0x11:
return Position.ALARM_POWER_OFF;
+ case 0x0C:
case 0x13:
+ case 0x25:
return Position.ALARM_TAMPERING;
case 0x14:
return Position.ALARM_DOOR;
+ case 0x18:
+ return Position.ALARM_REMOVING;
+ case 0x23:
+ return Position.ALARM_FALL_DOWN;
case 0x29:
return Position.ALARM_ACCELERATION;
case 0x30:
@@ -383,81 +442,27 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
return Position.ALARM_CORNERING;
case 0x2C:
return Position.ALARM_ACCIDENT;
- case 0x23:
- return Position.ALARM_FALL_DOWN;
default:
return null;
}
}
- private static final Pattern PATTERN_FUEL = new PatternBuilder()
- .text("!AIOIL,")
- .number("d+,") // device address
- .number("d+.d+,") // output value
- .number("(d+.d+),") // temperature
- .expression("[^,]+,") // version
- .number("dd") // back wave
- .number("d") // software status code
- .number("d,") // hardware status code
- .number("(d+.d+),") // measured value
- .expression("[01],") // movement status
- .number("d+,") // excited wave times
- .number("xx") // checksum
- .compile();
-
- private Position decodeFuelData(Position position, String sentence) {
- Parser parser = new Parser(PATTERN_FUEL, sentence);
- if (!parser.matches()) {
- return null;
- }
-
- position.set(Position.PREFIX_TEMP + 1, parser.nextDouble(0));
- position.set(Position.KEY_FUEL_LEVEL, parser.nextDouble(0));
-
- return position;
- }
-
- private static final Pattern PATTERN_LOCATION = new PatternBuilder()
- .text("Current position!")
- .number("Lat:([NS])(d+.d+),") // latitude
- .number("Lon:([EW])(d+.d+),") // longitude
- .text("Course:").number("(d+.d+),") // course
- .text("Speed:").number("(d+.d+),") // speed
- .text("DateTime:")
- .number("(dddd)-(dd)-(dd) +") // date
- .number("(dd):(dd):(dd)") // time
- .compile();
-
- private Position decodeLocationString(Position position, String sentence) {
- Parser parser = new Parser(PATTERN_LOCATION, sentence);
- if (!parser.matches()) {
- return null;
- }
-
- position.setValid(true);
- position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG));
- position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG));
- position.setCourse(parser.nextDouble());
- position.setSpeed(parser.nextDouble());
- position.setTime(parser.nextDateTime(Parser.DateTimeFormat.YMD_HMS));
-
- return position;
- }
-
- private Object decodeBasic(Channel channel, SocketAddress remoteAddress, ByteBuf buf) throws Exception {
+ private Object decodeBasic(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
int length = buf.readUnsignedByte();
int dataLength = length - 5;
int type = buf.readUnsignedByte();
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = null;
if (type != MSG_LOGIN) {
deviceSession = getDeviceSession(channel, remoteAddress);
if (deviceSession == null) {
return null;
}
- if (deviceSession.getTimeZone() == null) {
- deviceSession.setTimeZone(getTimeZone(deviceSession.getDeviceId()));
+ position.setDeviceId(deviceSession.getDeviceId());
+ if (!deviceSession.contains(DeviceSession.KEY_TIMEZONE)) {
+ deviceSession.set(DeviceSession.KEY_TIMEZONE, getTimeZone(deviceSession.getDeviceId()));
}
}
@@ -467,8 +472,8 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedShort(); // type
deviceSession = getDeviceSession(channel, remoteAddress, imei);
- if (deviceSession != null && deviceSession.getTimeZone() == null) {
- deviceSession.setTimeZone(getTimeZone(deviceSession.getDeviceId()));
+ if (deviceSession != null && !deviceSession.contains(DeviceSession.KEY_TIMEZONE)) {
+ deviceSession.set(DeviceSession.KEY_TIMEZONE, getTimeZone(deviceSession.getDeviceId()));
}
if (dataLength > 10) {
@@ -480,23 +485,21 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
offset = -offset;
}
if (deviceSession != null) {
- TimeZone timeZone = deviceSession.getTimeZone();
+ TimeZone timeZone = deviceSession.get(DeviceSession.KEY_TIMEZONE);
if (timeZone.getRawOffset() == 0) {
timeZone.setRawOffset(offset * 1000);
- deviceSession.setTimeZone(timeZone);
+ deviceSession.set(DeviceSession.KEY_TIMEZONE, timeZone);
}
}
-
}
if (deviceSession != null) {
sendResponse(channel, false, type, buf.getShort(buf.writerIndex() - 6), null);
}
- } else if (type == MSG_HEARTBEAT) {
+ return null;
- Position position = new Position(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
+ } else if (type == MSG_HEARTBEAT) {
getLastLocation(position, null);
@@ -525,6 +528,8 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
content.writeBytes(response.getBytes(StandardCharsets.US_ASCII));
sendResponse(channel, true, MSG_ADDRESS_RESPONSE, 0, content);
+ return null;
+
} else if (type == MSG_TIME_REQUEST) {
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
@@ -537,44 +542,13 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
content.writeByte(calendar.get(Calendar.SECOND));
sendResponse(channel, false, MSG_TIME_REQUEST, 0, content);
- } else if (type == MSG_X1_GPS || type == MSG_X1_PHOTO_INFO) {
-
- return decodeX1(channel, buf, deviceSession, type);
-
- } else if (type == MSG_WIFI || type == MSG_WIFI_2 || type == MSG_WIFI_4) {
-
- return decodeWifi(channel, buf, deviceSession, type);
-
- } else if (type == MSG_INFO) {
-
- Position position = new Position(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
-
- getLastLocation(position, null);
-
- position.set(Position.KEY_POWER, buf.readShort() * 0.01);
-
- return position;
-
- } else {
-
- return decodeBasicOther(channel, buf, deviceSession, type, dataLength);
-
- }
-
- return null;
- }
-
- private Object decodeX1(Channel channel, ByteBuf buf, DeviceSession deviceSession, int type) {
-
- if (type == MSG_X1_GPS) {
+ return null;
- Position position = new Position(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
+ } else if (type == MSG_X1_GPS) {
buf.readUnsignedInt(); // data and alarm
- decodeGps(position, buf, false, deviceSession.getTimeZone());
+ decodeGps(position, buf, false, deviceSession.get(DeviceSession.KEY_TIMEZONE));
buf.readUnsignedShort(); // terminal info
@@ -618,103 +592,111 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
photos.put(pictureId, photo);
sendPhotoRequest(channel, pictureId);
- }
-
- return null;
- }
+ return null;
- private Object decodeWifi(Channel channel, ByteBuf buf, DeviceSession deviceSession, int type) {
+ } else if (type == MSG_WIFI || type == MSG_WIFI_2 || type == MSG_WIFI_4) {
- Position position = new Position(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
+ ByteBuf time = buf.readSlice(6);
+ DateBuilder dateBuilder = new DateBuilder()
+ .setYear(BcdUtil.readInteger(time, 2))
+ .setMonth(BcdUtil.readInteger(time, 2))
+ .setDay(BcdUtil.readInteger(time, 2))
+ .setHour(BcdUtil.readInteger(time, 2))
+ .setMinute(BcdUtil.readInteger(time, 2))
+ .setSecond(BcdUtil.readInteger(time, 2));
+ getLastLocation(position, dateBuilder.getDate());
- ByteBuf time = buf.readSlice(6);
- DateBuilder dateBuilder = new DateBuilder()
- .setYear(BcdUtil.readInteger(time, 2))
- .setMonth(BcdUtil.readInteger(time, 2))
- .setDay(BcdUtil.readInteger(time, 2))
- .setHour(BcdUtil.readInteger(time, 2))
- .setMinute(BcdUtil.readInteger(time, 2))
- .setSecond(BcdUtil.readInteger(time, 2));
- getLastLocation(position, dateBuilder.getDate());
-
- Network network = new Network();
-
- int wifiCount;
- if (type == MSG_WIFI_4) {
- wifiCount = buf.readUnsignedByte();
- } else {
- wifiCount = buf.getUnsignedByte(2);
- }
+ Network network = new Network();
- for (int i = 0; i < wifiCount; i++) {
+ int wifiCount;
if (type == MSG_WIFI_4) {
- buf.skipBytes(2);
+ wifiCount = buf.readUnsignedByte();
+ } else {
+ wifiCount = buf.getUnsignedByte(2);
}
- WifiAccessPoint wifiAccessPoint = new WifiAccessPoint();
- wifiAccessPoint.setMacAddress(String.format("%02x:%02x:%02x:%02x:%02x:%02x",
- buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte(),
- buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()));
- if (type != MSG_WIFI_4) {
- wifiAccessPoint.setSignalStrength((int) buf.readUnsignedByte());
+
+ for (int i = 0; i < wifiCount; i++) {
+ if (type == MSG_WIFI_4) {
+ buf.skipBytes(2);
+ }
+ WifiAccessPoint wifiAccessPoint = new WifiAccessPoint();
+ wifiAccessPoint.setMacAddress(String.format("%02x:%02x:%02x:%02x:%02x:%02x",
+ buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte(),
+ buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()));
+ if (type != MSG_WIFI_4) {
+ wifiAccessPoint.setSignalStrength((int) buf.readUnsignedByte());
+ }
+ network.addWifiAccessPoint(wifiAccessPoint);
}
- network.addWifiAccessPoint(wifiAccessPoint);
- }
- if (type != MSG_WIFI_4) {
+ if (type != MSG_WIFI_4) {
- int cellCount = buf.readUnsignedByte();
- int mcc = buf.readUnsignedShort();
- int mnc = buf.readUnsignedByte();
- for (int i = 0; i < cellCount; i++) {
- network.addCellTower(CellTower.from(
- mcc, mnc, buf.readUnsignedShort(), buf.readUnsignedShort(), buf.readUnsignedByte()));
- }
+ int cellCount = buf.readUnsignedByte();
+ int mcc = buf.readUnsignedShort();
+ int mnc = buf.readUnsignedByte();
+ for (int i = 0; i < cellCount; i++) {
+ network.addCellTower(CellTower.from(
+ mcc, mnc, buf.readUnsignedShort(), buf.readUnsignedShort(), buf.readUnsignedByte()));
+ }
+
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeShort(0x7878);
+ response.writeByte(0);
+ response.writeByte(type);
+ response.writeBytes(time.resetReaderIndex());
+ response.writeByte('\r');
+ response.writeByte('\n');
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
- if (channel != null) {
- ByteBuf response = Unpooled.buffer();
- response.writeShort(0x7878);
- response.writeByte(0);
- response.writeByte(type);
- response.writeBytes(time.resetReaderIndex());
- response.writeByte('\r');
- response.writeByte('\n');
- channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
}
- }
+ position.setNetwork(network);
- position.setNetwork(network);
+ return position;
- return position;
- }
+ } else if (type == MSG_INFO) {
- private Object decodeBasicOther(
- Channel channel, ByteBuf buf, DeviceSession deviceSession, int type, int dataLength) {
+ getLastLocation(position, null);
- Position position = new Position(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
+ position.set(Position.KEY_POWER, buf.readShort() * 0.01);
+
+ return position;
+
+ } else if (type == MSG_LBS_MULTIPLE_3 && variant == Variant.SR411_MINI) {
+
+ decodeGps(position, buf, false, deviceSession.get(DeviceSession.KEY_TIMEZONE));
- if (type == MSG_LBS_STATUS && dataLength >= 18) {
+ decodeLbs(position, buf, type, false);
- return null; // space10x multi-lbs message
+ position.set(Position.KEY_IGNITION, buf.readUnsignedByte() > 0);
+ position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01);
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.01);
+
+ return position;
- } else if (type == MSG_LBS_MULTIPLE_1 || type == MSG_LBS_MULTIPLE_2 || type == MSG_LBS_EXTEND
- || type == MSG_LBS_WIFI || type == MSG_LBS_2 || type == MSG_WIFI_3 || type == MSG_WIFI_5) {
+ } 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_WIFI_3 || type == MSG_WIFI_5) {
boolean longFormat = type == MSG_LBS_2 || type == MSG_WIFI_3 || type == MSG_WIFI_5;
- DateBuilder dateBuilder = new DateBuilder(deviceSession.getTimeZone())
+ DateBuilder dateBuilder = new DateBuilder((TimeZone) deviceSession.get(DeviceSession.KEY_TIMEZONE))
.setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
.setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
getLastLocation(position, dateBuilder.getDate());
+ if (variant == Variant.WANWAY_S20) {
+ buf.readUnsignedByte(); // ta
+ }
+
int mcc = buf.readUnsignedShort();
int mnc = BitUtil.check(mcc, 15) ? buf.readUnsignedShort() : buf.readUnsignedByte();
Network network = new Network();
- int cellCount = type == MSG_WIFI_5 ? 6 : 7;
+ int cellCount = variant == Variant.WANWAY_S20 ? buf.readUnsignedByte() : type == MSG_WIFI_5 ? 6 : 7;
for (int i = 0; i < cellCount; i++) {
int lac = longFormat ? buf.readInt() : buf.readUnsignedShort();
int cid = longFormat ? (int) buf.readLong() : buf.readUnsignedMedium();
@@ -724,9 +706,12 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
}
}
- buf.readUnsignedByte(); // time leads
+ if (variant != Variant.WANWAY_S20) {
+ buf.readUnsignedByte(); // ta
+ }
- if (type != MSG_LBS_MULTIPLE_1 && type != MSG_LBS_MULTIPLE_2 && type != MSG_LBS_2) {
+ if (type != MSG_LBS_MULTIPLE_1 && type != MSG_LBS_MULTIPLE_2 && type != MSG_LBS_MULTIPLE_3
+ && type != MSG_LBS_2) {
int wifiCount = buf.readUnsignedByte();
for (int i = 0; i < wifiCount; i++) {
String mac = ByteBufUtil.hexDump(buf.readSlice(6)).replaceAll("(..)", "$1:");
@@ -753,7 +738,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
}
}
- } else if (type == MSG_BMS || type == MSG_BMS_2) {
+ } else if (type == MSG_BMS) {
buf.skipBytes(8); // serial number
@@ -785,20 +770,144 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
return position;
+ } else if (type == MSG_STATUS && buf.readableBytes() == 22) {
+
+ getLastLocation(position, null);
+
+ buf.readUnsignedByte(); // information content
+ buf.readUnsignedShort(); // satellites
+ buf.readUnsignedByte(); // alarm
+ buf.readUnsignedByte(); // language
+
+ position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
+
+ buf.readUnsignedByte(); // working mode
+ buf.readUnsignedShort(); // working voltage
+ buf.readUnsignedByte(); // reserved
+ buf.readUnsignedShort(); // working times
+ buf.readUnsignedShort(); // working time
+
+ int value = buf.readUnsignedShort();
+ double temperature = BitUtil.to(value, 15) * 0.1;
+ position.set(Position.PREFIX_TEMP + 1, BitUtil.check(value, 15) ? temperature : -temperature);
+
} else if (isSupported(type)) {
- if (type == MSG_STATUS && buf.readableBytes() == 22) {
- decodeHeartbeat(buf, position);
+ if (type == MSG_LBS_STATUS && variant == Variant.SPACE10X) {
+ return null; // multi-lbs message
+ }
+
+ if (hasGps(type)) {
+ decodeGps(position, buf, false, deviceSession.get(DeviceSession.KEY_TIMEZONE));
} else {
- decodeBasicUniversal(buf, deviceSession, type, position);
+ getLastLocation(position, null);
+ }
+
+ if (hasLbs(type) && buf.readableBytes() > 6) {
+ decodeLbs(position, buf, type, hasStatus(type) && type != MSG_LBS_ALARM);
+ }
+
+ if (hasStatus(type)) {
+ decodeStatus(position, buf);
+ if (variant == Variant.OBD6) {
+ int signal = buf.readUnsignedShort();
+ int satellites = BitUtil.between(signal, 10, 15) + BitUtil.between(signal, 5, 10);
+ position.set(Position.KEY_SATELLITES, satellites);
+ position.set(Position.KEY_RSSI, BitUtil.to(signal, 5));
+ position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
+ buf.readUnsignedByte(); // language
+ position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
+ buf.readUnsignedByte(); // working mode
+ position.set(Position.KEY_POWER, buf.readUnsignedShort() / 100.0);
+ } else {
+ position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte() * 100 / 6);
+ position.set(Position.KEY_RSSI, buf.readUnsignedByte());
+ position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
+ }
+ }
+
+ if (type == MSG_GPS_LBS_1) {
+ if (variant == Variant.GT06E_CARD) {
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+ String data = buf.readCharSequence(buf.readUnsignedByte(), StandardCharsets.US_ASCII).toString();
+ buf.readUnsignedByte(); // alarm
+ buf.readUnsignedByte(); // swiped
+ position.set("driverLicense", data.trim());
+ } else if (variant == Variant.BENWAY) {
+ int mask = buf.readUnsignedShort();
+ position.set(Position.KEY_IGNITION, BitUtil.check(mask, 8 + 7));
+ position.set(Position.PREFIX_IN + 2, BitUtil.check(mask, 8 + 6));
+ if (BitUtil.check(mask, 8 + 4)) {
+ int value = BitUtil.to(mask, 8 + 1);
+ if (BitUtil.check(mask, 8 + 1)) {
+ value = -value;
+ }
+ position.set(Position.PREFIX_TEMP + 1, value);
+ } else {
+ int value = BitUtil.to(mask, 8 + 2);
+ if (BitUtil.check(mask, 8 + 5)) {
+ position.set(Position.PREFIX_ADC + 1, value);
+ } else {
+ position.set(Position.PREFIX_ADC + 1, value * 0.1);
+ }
+ }
+ } else if (variant == Variant.VXT01) {
+ decodeStatus(position, buf);
+ position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01);
+ position.set(Position.KEY_RSSI, buf.readUnsignedByte());
+ buf.readUnsignedByte(); // alarm extension
+ } else if (variant == Variant.S5) {
+ decodeStatus(position, buf);
+ position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01);
+ position.set(Position.KEY_RSSI, buf.readUnsignedByte());
+ position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
+ position.set("oil", buf.readUnsignedShort());
+ int temperature = buf.readUnsignedByte();
+ if (BitUtil.check(temperature, 7)) {
+ temperature = -BitUtil.to(temperature, 7);
+ }
+ position.set(Position.PREFIX_TEMP + 1, temperature);
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 10);
+ }
+ }
+
+ if ((type == MSG_GPS_LBS_2 || type == MSG_GPS_LBS_3 || type == MSG_GPS_LBS_4)
+ && buf.readableBytes() >= 3 + 6) {
+ position.set(Position.KEY_IGNITION, buf.readUnsignedByte() > 0);
+ position.set(Position.KEY_EVENT, buf.readUnsignedByte()); // reason
+ position.set(Position.KEY_ARCHIVE, buf.readUnsignedByte() > 0);
+ }
+
+ if (type == MSG_GPS_LBS_3) {
+ int module = buf.readUnsignedShort();
+ int subLength = buf.readUnsignedByte();
+ switch (module) {
+ case 0x0027:
+ position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01);
+ break;
+ case 0x002E:
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+ break;
+ case 0x003B:
+ position.setAccuracy(buf.readUnsignedShort() * 0.01);
+ break;
+ default:
+ buf.skipBytes(subLength);
+ break;
+ }
+ }
+
+ if (buf.readableBytes() == 4 + 6) {
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
}
} else if (type == MSG_ALARM) {
+
boolean extendedAlarm = dataLength > 7;
if (extendedAlarm) {
- decodeGps(position, buf, false, false, false, deviceSession.getTimeZone());
+ decodeGps(position, buf, false, false, false, deviceSession.get(DeviceSession.KEY_TIMEZONE));
} else {
- DateBuilder dateBuilder = new DateBuilder(deviceSession.getTimeZone())
+ DateBuilder dateBuilder = new DateBuilder((TimeZone) deviceSession.get(DeviceSession.KEY_TIMEZONE))
.setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
.setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
getLastLocation(position, dateBuilder.getDate());
@@ -830,6 +939,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
break;
}
+
} else {
if (dataLength > 0) {
@@ -855,114 +965,6 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
return position;
}
- private void decodeHeartbeat(ByteBuf buf, Position position) {
-
- getLastLocation(position, null);
-
- buf.readUnsignedByte(); // information content
- buf.readUnsignedShort(); // satellites
- buf.readUnsignedByte(); // alarm
- buf.readUnsignedByte(); // language
-
- position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
-
- buf.readUnsignedByte(); // working mode
- buf.readUnsignedShort(); // working voltage
- buf.readUnsignedByte(); // reserved
- buf.readUnsignedShort(); // working times
- buf.readUnsignedShort(); // working time
-
- int value = buf.readUnsignedShort();
- double temperature = BitUtil.to(value, 15) * 0.1;
- position.set(Position.PREFIX_TEMP + 1, BitUtil.check(value, 15) ? temperature : -temperature);
-
- }
-
- private void decodeBasicUniversal(ByteBuf buf, DeviceSession deviceSession, int type, Position position) {
-
- if (hasGps(type)) {
- decodeGps(position, buf, false, deviceSession.getTimeZone());
- } else {
- getLastLocation(position, null);
- }
-
- if (hasLbs(type)) {
- decodeLbs(position, buf, type, hasStatus(type));
- }
-
- if (hasStatus(type)) {
- decodeStatus(position, buf, true);
- }
-
- if (type == MSG_GPS_LBS_1 && buf.readableBytes() > 75 + 6) {
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
- String data = buf.readCharSequence(buf.readUnsignedByte(), StandardCharsets.US_ASCII).toString();
- buf.readUnsignedByte(); // alarm
- buf.readUnsignedByte(); // swiped
- position.set("driverLicense", data.trim());
- }
-
- if (type == MSG_GPS_LBS_1 && buf.readableBytes() == 18) {
- decodeStatus(position, buf, false);
- position.set("oil", buf.readUnsignedShort());
- int temperature = buf.readUnsignedByte();
- if (BitUtil.check(temperature, 7)) {
- temperature = -BitUtil.to(temperature, 7);
- }
- position.set(Position.PREFIX_TEMP + 1, temperature);
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 10);
- }
-
- if (type == MSG_GPS_LBS_1 && buf.readableBytes() == 2 + 6) {
- int mask = buf.readUnsignedShort();
- position.set(Position.KEY_IGNITION, BitUtil.check(mask, 8 + 7));
- position.set(Position.PREFIX_IN + 2, BitUtil.check(mask, 8 + 6));
- if (BitUtil.check(mask, 8 + 4)) {
- int value = BitUtil.to(mask, 8 + 1);
- if (BitUtil.check(mask, 8 + 1)) {
- value = -value;
- }
- position.set(Position.PREFIX_TEMP + 1, value);
- } else {
- int value = BitUtil.to(mask, 8 + 2);
- if (BitUtil.check(mask, 8 + 5)) {
- position.set(Position.PREFIX_ADC + 1, value);
- } else {
- position.set(Position.PREFIX_ADC + 1, value * 0.1);
- }
- }
- }
-
- if ((type == MSG_GPS_LBS_2 || type == MSG_GPS_LBS_3 || type == MSG_GPS_LBS_4) && buf.readableBytes() >= 3 + 6) {
- position.set(Position.KEY_IGNITION, buf.readUnsignedByte() > 0);
- position.set(Position.KEY_EVENT, buf.readUnsignedByte()); // reason
- position.set(Position.KEY_ARCHIVE, buf.readUnsignedByte() > 0);
- }
-
- if (type == MSG_GPS_LBS_3) {
- int module = buf.readUnsignedShort();
- int length = buf.readUnsignedByte();
- switch (module) {
- case 0x0027:
- position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01);
- break;
- case 0x002E:
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
- break;
- case 0x003B:
- position.setAccuracy(buf.readUnsignedShort() * 0.01);
- break;
- default:
- buf.skipBytes(length);
- break;
- }
- }
-
- if (buf.readableBytes() == 4 + 6) {
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
- }
- }
-
private Object decodeExtended(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
@@ -970,8 +972,8 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- if (deviceSession.getTimeZone() == null) {
- deviceSession.setTimeZone(getTimeZone(deviceSession.getDeviceId()));
+ if (!deviceSession.contains(DeviceSession.KEY_TIMEZONE)) {
+ deviceSession.set(DeviceSession.KEY_TIMEZONE, getTimeZone(deviceSession.getDeviceId()));
}
Position position = new Position(getProtocolName());
@@ -990,7 +992,16 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
data = buf.readSlice(buf.readableBytes() - 6).toString(StandardCharsets.UTF_16BE);
}
- if (decodeLocationString(position, data) == null) {
+ Parser parser = new Parser(PATTERN_LOCATION, data);
+
+ if (parser.matches()) {
+ position.setValid(true);
+ position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG));
+ position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG));
+ position.setCourse(parser.nextDouble());
+ position.setSpeed(parser.nextDouble());
+ position.setTime(parser.nextDateTime(Parser.DateTimeFormat.YMD_HMS));
+ } else {
getLastLocation(position, null);
position.set(Position.KEY_RESULT, data);
}
@@ -1004,31 +1015,62 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
getLastLocation(position, null);
if (subType == 0x00) {
+
position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort() * 0.01);
return position;
+
} else if (subType == 0x05) {
+
+ if (buf.readableBytes() >= 6 + 1 + 6) {
+ DateBuilder dateBuilder = new DateBuilder((TimeZone) deviceSession.get(DeviceSession.KEY_TIMEZONE))
+ .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
+ position.setDeviceTime(dateBuilder.getDate());
+ }
+
int flags = buf.readUnsignedByte();
position.set(Position.KEY_DOOR, BitUtil.check(flags, 0));
position.set(Position.PREFIX_IO + 1, BitUtil.check(flags, 2));
return position;
+
} else if (subType == 0x0a) {
+
buf.skipBytes(8); // imei
buf.skipBytes(8); // imsi
position.set(Position.KEY_ICCID, ByteBufUtil.hexDump(buf.readSlice(10)).replaceAll("f", ""));
return position;
+
} else if (subType == 0x0d) {
+
if (buf.getByte(buf.readerIndex()) != '!') {
buf.skipBytes(6);
}
- return decodeFuelData(position, buf.toString(
+
+ Parser parser = new Parser(PATTERN_FUEL, buf.toString(
buf.readerIndex(), buf.readableBytes() - 4 - 2, StandardCharsets.US_ASCII));
+ if (!parser.matches()) {
+ return null;
+ }
+
+ position.set(Position.PREFIX_TEMP + 1, parser.nextDouble(0));
+ position.set(Position.KEY_FUEL_LEVEL, parser.nextDouble(0));
+
+ return position;
+
} else if (subType == 0x1b) {
- buf.readUnsignedByte(); // header
- buf.readUnsignedByte(); // type
- position.set(Position.KEY_DRIVER_UNIQUE_ID, ByteBufUtil.hexDump(buf.readSlice(4)));
- buf.readUnsignedByte(); // checksum
- buf.readUnsignedByte(); // footer
+
+ if (Character.isLetter(buf.getUnsignedByte(buf.readerIndex()))) {
+ String data = buf.readCharSequence(buf.readableBytes() - 6, StandardCharsets.US_ASCII).toString();
+ position.set("serial", data.trim());
+ } else {
+ buf.readUnsignedByte(); // header
+ buf.readUnsignedByte(); // type
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, ByteBufUtil.hexDump(buf.readSlice(4)));
+ buf.readUnsignedByte(); // checksum
+ buf.readUnsignedByte(); // footer
+ }
return position;
+
}
} else if (type == MSG_X1_PHOTO_DATA) {
@@ -1043,15 +1085,13 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
if (photo.writableBytes() > 0) {
sendPhotoRequest(channel, pictureId);
} else {
- Device device = Context.getDeviceManager().getById(deviceSession.getDeviceId());
- position.set(
- Position.KEY_IMAGE, Context.getMediaManager().writeFile(device.getUniqueId(), photo, "jpg"));
+ position.set(Position.KEY_IMAGE, writeMediaFile(deviceSession.getUniqueId(), photo, "jpg"));
photos.remove(pictureId).release();
}
} else if (type == MSG_AZ735_GPS || type == MSG_AZ735_ALARM) {
- if (!decodeGps(position, buf, true, deviceSession.getTimeZone())) {
+ if (!decodeGps(position, buf, true, deviceSession.get(DeviceSession.KEY_TIMEZONE))) {
getLastLocation(position, position.getDeviceTime());
}
@@ -1096,7 +1136,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
} else if (type == MSG_OBD) {
- DateBuilder dateBuilder = new DateBuilder(deviceSession.getTimeZone())
+ DateBuilder dateBuilder = new DateBuilder((TimeZone) deviceSession.get(DeviceSession.KEY_TIMEZONE))
.setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
.setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
@@ -1143,132 +1183,111 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
} else if (type == MSG_GPS_MODULAR) {
- return decodeExtendedModular(channel, buf, deviceSession);
-
- } else {
-
- return decodeExtendedOther(channel, buf, deviceSession, type);
-
- }
-
- return null;
- }
-
- private Object decodeExtendedModular(Channel channel, ByteBuf buf, DeviceSession deviceSession) {
-
- Position position = new Position(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
-
- while (buf.readableBytes() > 6) {
- int moduleType = buf.readUnsignedShort();
- int moduleLength = buf.readUnsignedShort();
-
- switch (moduleType) {
- case 0x03:
- position.set(Position.KEY_ICCID, ByteBufUtil.hexDump(buf.readSlice(10)));
- break;
- case 0x09:
- position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
- break;
- case 0x0a:
- position.set(Position.KEY_SATELLITES_VISIBLE, buf.readUnsignedByte());
- break;
- case 0x11:
- CellTower cellTower = CellTower.from(
- buf.readUnsignedShort(),
- buf.readUnsignedShort(),
- buf.readUnsignedShort(),
- buf.readUnsignedMedium(),
- buf.readUnsignedByte());
- if (cellTower.getCellId() > 0) {
- position.setNetwork(new Network(cellTower));
- }
- break;
- case 0x18:
- position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.01);
- break;
- case 0x28:
- position.set(Position.KEY_HDOP, buf.readUnsignedByte() * 0.1);
- break;
- case 0x29:
- position.set(Position.KEY_INDEX, buf.readUnsignedInt());
- break;
- case 0x2a:
- int input = buf.readUnsignedByte();
- position.set(Position.KEY_DOOR, BitUtil.to(input, 4) > 0);
- position.set("tamper", BitUtil.from(input, 4) > 0);
- break;
- case 0x2b:
- int event = buf.readUnsignedByte();
- switch (event) {
- case 0x11:
- position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY);
- break;
- case 0x12:
- position.set(Position.KEY_ALARM, Position.ALARM_LOW_POWER);
- break;
- case 0x13:
- position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT);
- break;
- case 0x14:
- position.set(Position.KEY_ALARM, Position.ALARM_REMOVING);
- break;
- default:
- break;
- }
- position.set(Position.KEY_EVENT, event);
- break;
- case 0x2e:
- position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
- break;
- case 0x33:
- position.setTime(new Date(buf.readUnsignedInt() * 1000));
- position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
- position.setAltitude(buf.readShort());
-
- double latitude = buf.readUnsignedInt() / 60.0 / 30000.0;
- double longitude = buf.readUnsignedInt() / 60.0 / 30000.0;
- position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
-
- int flags = buf.readUnsignedShort();
- position.setCourse(BitUtil.to(flags, 10));
- position.setValid(BitUtil.check(flags, 12));
+ while (buf.readableBytes() > 6) {
+ int moduleType = buf.readUnsignedShort();
+ int moduleLength = buf.readUnsignedShort();
- if (!BitUtil.check(flags, 10)) {
- latitude = -latitude;
- }
- if (BitUtil.check(flags, 11)) {
- longitude = -longitude;
- }
-
- position.setLatitude(latitude);
- position.setLongitude(longitude);
- break;
- case 0x34:
- position.set(Position.KEY_EVENT, buf.readUnsignedByte());
- buf.readUnsignedIntLE(); // time
- buf.skipBytes(buf.readUnsignedByte()); // content
- break;
- default:
- buf.skipBytes(moduleLength);
- break;
+ switch (moduleType) {
+ case 0x03:
+ position.set(Position.KEY_ICCID, ByteBufUtil.hexDump(buf.readSlice(10)));
+ break;
+ case 0x09:
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ break;
+ case 0x0a:
+ position.set(Position.KEY_SATELLITES_VISIBLE, buf.readUnsignedByte());
+ break;
+ case 0x11:
+ CellTower cellTower = CellTower.from(
+ buf.readUnsignedShort(),
+ buf.readUnsignedShort(),
+ buf.readUnsignedShort(),
+ buf.readUnsignedMedium(),
+ buf.readUnsignedByte());
+ if (cellTower.getCellId() > 0) {
+ position.setNetwork(new Network(cellTower));
+ }
+ break;
+ case 0x18:
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.01);
+ break;
+ case 0x28:
+ position.set(Position.KEY_HDOP, buf.readUnsignedByte() * 0.1);
+ break;
+ case 0x29:
+ position.set(Position.KEY_INDEX, buf.readUnsignedInt());
+ break;
+ case 0x2a:
+ int input = buf.readUnsignedByte();
+ position.set(Position.KEY_DOOR, BitUtil.to(input, 4) > 0);
+ position.set("tamper", BitUtil.from(input, 4) > 0);
+ break;
+ case 0x2b:
+ int event = buf.readUnsignedByte();
+ switch (event) {
+ case 0x11:
+ position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY);
+ break;
+ case 0x12:
+ position.set(Position.KEY_ALARM, Position.ALARM_LOW_POWER);
+ break;
+ case 0x13:
+ position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT);
+ break;
+ case 0x14:
+ position.set(Position.KEY_ALARM, Position.ALARM_REMOVING);
+ break;
+ default:
+ break;
+ }
+ position.set(Position.KEY_EVENT, event);
+ break;
+ case 0x2e:
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
+ break;
+ case 0x33:
+ position.setTime(new Date(buf.readUnsignedInt() * 1000));
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ position.setAltitude(buf.readShort());
+
+ double latitude = buf.readUnsignedInt() / 60.0 / 30000.0;
+ double longitude = buf.readUnsignedInt() / 60.0 / 30000.0;
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
+
+ int flags = buf.readUnsignedShort();
+ position.setCourse(BitUtil.to(flags, 10));
+ position.setValid(BitUtil.check(flags, 12));
+
+ if (!BitUtil.check(flags, 10)) {
+ latitude = -latitude;
+ }
+ if (BitUtil.check(flags, 11)) {
+ longitude = -longitude;
+ }
+
+ position.setLatitude(latitude);
+ position.setLongitude(longitude);
+ break;
+ case 0x34:
+ position.set(Position.KEY_EVENT, buf.readUnsignedByte());
+ buf.readUnsignedIntLE(); // time
+ buf.skipBytes(buf.readUnsignedByte()); // content
+ break;
+ default:
+ buf.skipBytes(moduleLength);
+ break;
+ }
}
- }
-
- if (position.getFixTime() == null) {
- getLastLocation(position, null);
- }
-
- sendResponse(channel, false, MSG_GPS_MODULAR, buf.readUnsignedShort(), null);
- return position;
- }
+ if (position.getFixTime() == null) {
+ getLastLocation(position, null);
+ }
- private Object decodeExtendedOther(Channel channel, ByteBuf buf, DeviceSession deviceSession, int type) {
+ sendResponse(channel, false, MSG_GPS_MODULAR, buf.readUnsignedShort(), null);
- Position position = null;
+ return position;
- if (type == MSG_MULTIMEDIA || type == MSG_MULTIMEDIA_2) {
+ } else if (type == MSG_MULTIMEDIA) {
buf.skipBytes(8); // serial number
long timestamp = buf.readUnsignedInt() * 1000;
@@ -1301,9 +1320,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
getLastLocation(position, new Date(timestamp));
- Device device = Context.getDeviceManager().getById(deviceSession.getDeviceId());
- position.set(Position.KEY_IMAGE,
- Context.getMediaManager().writeFile(device.getUniqueId(), photo, "jpg"));
+ position.set(Position.KEY_IMAGE, writeMediaFile(deviceSession.getUniqueId(), photo, "jpg"));
photos.remove(mediaId).release();
}
}
@@ -1343,21 +1360,56 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
+ private void decodeVariant(ByteBuf buf) {
+ int header = buf.getUnsignedShort(buf.readerIndex());
+ int length;
+ int type;
+ if (header == 0x7878) {
+ length = buf.getUnsignedByte(buf.readerIndex() + 2);
+ type = buf.getUnsignedByte(buf.readerIndex() + 2 + 1);
+ } else {
+ length = buf.getUnsignedShort(buf.readerIndex() + 2);
+ type = buf.getUnsignedByte(buf.readerIndex() + 2 + 2);
+ }
+
+ if (header == 0x7878 && type == MSG_GPS_LBS_1 && length == 0x24) {
+ variant = Variant.VXT01;
+ } else if (header == 0x7878 && type == MSG_GPS_LBS_STATUS_1 && length == 0x24) {
+ variant = Variant.VXT01;
+ } else if (header == 0x7878 && type == MSG_LBS_MULTIPLE_3 && length == 0x31) {
+ variant = Variant.WANWAY_S20;
+ } else if (header == 0x7878 && type == MSG_LBS_MULTIPLE_3 && length == 0x2e) {
+ variant = Variant.SR411_MINI;
+ } else if (header == 0x7878 && type == MSG_GPS_LBS_1 && length >= 0x71) {
+ variant = Variant.GT06E_CARD;
+ } else if (header == 0x7878 && type == MSG_GPS_LBS_1 && length == 0x21) {
+ variant = Variant.BENWAY;
+ } else if (header == 0x7878 && type == MSG_GPS_LBS_1 && length == 0x2b) {
+ variant = Variant.S5;
+ } else if (header == 0x7878 && type == MSG_LBS_STATUS && length >= 0x17) {
+ variant = Variant.SPACE10X;
+ } else if (header == 0x7878 && type == MSG_STATUS && length == 0x13) {
+ variant = Variant.OBD6;
+ } else {
+ variant = Variant.STANDARD;
+ }
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
+ decodeVariant(buf);
+
int header = buf.readShort();
if (header == 0x7878) {
return decodeBasic(channel, remoteAddress, buf);
- } else if (header == 0x7979) {
+ } else {
return decodeExtended(channel, remoteAddress, buf);
}
-
- return null;
}
}
diff --git a/src/main/java/org/traccar/protocol/Gt06ProtocolEncoder.java b/src/main/java/org/traccar/protocol/Gt06ProtocolEncoder.java
index 9115ba10f..dc5dd446f 100644
--- a/src/main/java/org/traccar/protocol/Gt06ProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/Gt06ProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,10 +18,12 @@ package org.traccar.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.traccar.BaseProtocolEncoder;
-import org.traccar.Context;
+import org.traccar.Protocol;
+import org.traccar.config.Keys;
import org.traccar.helper.Checksum;
+import org.traccar.helper.model.AttributeUtil;
import org.traccar.model.Command;
-import org.traccar.Protocol;
+import org.traccar.model.Device;
import java.nio.charset.StandardCharsets;
@@ -33,8 +35,8 @@ public class Gt06ProtocolEncoder extends BaseProtocolEncoder {
private ByteBuf encodeContent(long deviceId, String content) {
- boolean language = Context.getIdentityManager()
- .lookupAttributeBoolean(deviceId, getProtocolName() + ".language", false, false, true);
+ boolean language = AttributeUtil.lookup(
+ getCacheManager(), Keys.PROTOCOL_LANGUAGE.withPrefix(getProtocolName()), deviceId);
ByteBuf buf = Unpooled.buffer();
@@ -43,7 +45,7 @@ public class Gt06ProtocolEncoder extends BaseProtocolEncoder {
buf.writeByte(1 + 1 + 4 + content.length() + 2 + 2 + (language ? 2 : 0)); // message length
- buf.writeByte(0x80); // message type
+ buf.writeByte(Gt06ProtocolDecoder.MSG_COMMAND_0);
buf.writeByte(4 + content.length()); // command length
buf.writeInt(0);
@@ -66,19 +68,31 @@ public class Gt06ProtocolEncoder extends BaseProtocolEncoder {
@Override
protected Object encodeCommand(Command command) {
- boolean alternative = Context.getIdentityManager().lookupAttributeBoolean(
- command.getDeviceId(), getProtocolName() + ".alternative", false, false, true);
+ boolean alternative = AttributeUtil.lookup(
+ getCacheManager(), Keys.PROTOCOL_ALTERNATIVE.withPrefix(getProtocolName()), command.getDeviceId());
+
+ String password = AttributeUtil.getDevicePassword(
+ getCacheManager(), command.getDeviceId(), getProtocolName(), "123456");
- String password = Context.getIdentityManager()
- .getDevicePassword(command.getDeviceId(), getProtocolName(), "123456");
+ Device device = getCacheManager().getObject(Device.class, command.getDeviceId());
switch (command.getType()) {
case Command.TYPE_ENGINE_STOP:
- return encodeContent(command.getDeviceId(),
- alternative ? "DYD," + password + "#" : "Relay,1#");
+ if ("G109".equals(device.getModel())) {
+ return encodeContent(command.getDeviceId(), "DYD#");
+ } else if (alternative) {
+ return encodeContent(command.getDeviceId(), "DYD," + password + "#");
+ } else {
+ return encodeContent(command.getDeviceId(), "Relay,1#");
+ }
case Command.TYPE_ENGINE_RESUME:
- return encodeContent(command.getDeviceId(),
- alternative ? "HFYD," + password + "#" : "Relay,0#");
+ if ("G109".equals(device.getModel())) {
+ return encodeContent(command.getDeviceId(), "HFYD#");
+ } else if (alternative) {
+ return encodeContent(command.getDeviceId(), "HFYD," + password + "#");
+ } else {
+ return encodeContent(command.getDeviceId(), "Relay,0#");
+ }
case Command.TYPE_CUSTOM:
return encodeContent(command.getDeviceId(), command.getString(Command.KEY_DATA));
default:
diff --git a/src/main/java/org/traccar/protocol/Gt30Protocol.java b/src/main/java/org/traccar/protocol/Gt30Protocol.java
index aa4ad20b1..6b79ba58b 100644
--- a/src/main/java/org/traccar/protocol/Gt30Protocol.java
+++ b/src/main/java/org/traccar/protocol/Gt30Protocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class Gt30Protocol extends BaseProtocol {
- public Gt30Protocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public Gt30Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/Gt30ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt30ProtocolDecoder.java
index abf208a46..fb3a2b8ae 100644
--- a/src/main/java/org/traccar/protocol/Gt30ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gt30ProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/H02Protocol.java b/src/main/java/org/traccar/protocol/H02Protocol.java
index a5246abc6..4e5f8c96a 100644
--- a/src/main/java/org/traccar/protocol/H02Protocol.java
+++ b/src/main/java/org/traccar/protocol/H02Protocol.java
@@ -17,15 +17,18 @@ package org.traccar.protocol;
import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
-import org.traccar.Context;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class H02Protocol extends BaseProtocol {
- public H02Protocol() {
+ @Inject
+ public H02Protocol(Config config) {
setSupportedDataCommands(
Command.TYPE_ALARM_ARM,
Command.TYPE_ALARM_DISARM,
@@ -33,19 +36,19 @@ public class H02Protocol extends BaseProtocol {
Command.TYPE_ENGINE_RESUME,
Command.TYPE_POSITION_PERIODIC
);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
- int messageLength = Context.getConfig().getInteger(Keys.PROTOCOL_MESSAGE_LENGTH.withPrefix(getName()));
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
+ int messageLength = config.getInteger(Keys.PROTOCOL_MESSAGE_LENGTH.withPrefix(getName()));
pipeline.addLast(new H02FrameDecoder(messageLength));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new H02ProtocolEncoder(H02Protocol.this));
pipeline.addLast(new H02ProtocolDecoder(H02Protocol.this));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringEncoder());
pipeline.addLast(new H02ProtocolEncoder(H02Protocol.this));
pipeline.addLast(new H02ProtocolDecoder(H02Protocol.this));
diff --git a/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java b/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java
index dd7141a2c..2ad4f644b 100644
--- a/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,8 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.config.Keys;
@@ -176,17 +175,19 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
.number("(d+),") // coding scheme
.groupEnd()
.groupBegin()
- .number("-(d+)-(d+.d+),") // latitude
+ .number("-(d+)-(d+.d+),([NS]),") // latitude
.or()
- .number("(d+)(dd.d+),") // latitude
+ .number("(d+)(dd.d+),([NS]),") // latitude
+ .or()
+ .number("(d+)(dd)(d{4}),([NS]),") // latitude
.groupEnd()
- .expression("([NS]),")
.groupBegin()
- .number("-(d+)-(d+.d+),") // longitude
+ .number("-(d+)-(d+.d+),([EW]),") // longitude
.or()
- .number("(d+)(dd.d+),") // longitude
+ .number("(d+)(dd.d+),([EW]),") // longitude
+ .or()
+ .number("(d+)(dd)(d{4}),([EW]),") // longitude
.groupEnd()
- .expression("([EW]),")
.number(" *(d+.?d*),") // speed
.number("(d+.?d*)?,") // course
.number("(?:d+,)?") // battery
@@ -332,7 +333,7 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
if (parser.hasNext() && parser.next().equals("V1")) {
sendResponse(channel, remoteAddress, id, "V1");
- } else if (Context.getConfig().getBoolean(Keys.PROTOCOL_ACK.withPrefix(getProtocolName()))) {
+ } else if (getConfig().getBoolean(Keys.PROTOCOL_ACK.withPrefix(getProtocolName()))) {
sendResponse(channel, remoteAddress, id, "R12");
}
@@ -349,19 +350,25 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
position.setValid(true);
}
- if (parser.hasNext(2)) {
- position.setLatitude(-parser.nextCoordinate());
+ if (parser.hasNext(3)) {
+ position.setLatitude(parser.nextCoordinate());
}
- if (parser.hasNext(2)) {
+ if (parser.hasNext(3)) {
position.setLatitude(parser.nextCoordinate());
}
+ if (parser.hasNext(4)) {
+ position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_MIN_MIN_HEM));
+ }
- if (parser.hasNext(2)) {
- position.setLongitude(-parser.nextCoordinate());
+ if (parser.hasNext(3)) {
+ position.setLongitude(parser.nextCoordinate());
}
- if (parser.hasNext(2)) {
+ if (parser.hasNext(3)) {
position.setLongitude(parser.nextCoordinate());
}
+ if (parser.hasNext(4)) {
+ position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_MIN_MIN_HEM));
+ }
position.setSpeed(parser.nextDouble(0));
position.setCourse(parser.nextDouble(0));
@@ -384,7 +391,8 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
position.setAltitude(parser.nextInt(0));
- position.setNetwork(new Network(CellTower.fromLacCid(parser.nextHexInt(0), parser.nextHexInt(0))));
+ position.setNetwork(new Network(CellTower.fromLacCid(
+ getConfig(), parser.nextHexInt(0), parser.nextHexInt(0))));
}
if (parser.hasNext()) {
diff --git a/src/main/java/org/traccar/protocol/H02ProtocolEncoder.java b/src/main/java/org/traccar/protocol/H02ProtocolEncoder.java
index 7a765332c..86b8c80d4 100644
--- a/src/main/java/org/traccar/protocol/H02ProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/H02ProtocolEncoder.java
@@ -6,7 +6,7 @@
* 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
+ * 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,
@@ -16,10 +16,11 @@
*/
package org.traccar.protocol;
-import org.traccar.Context;
+import org.traccar.Protocol;
import org.traccar.StringProtocolEncoder;
+import org.traccar.config.Keys;
+import org.traccar.helper.model.AttributeUtil;
import org.traccar.model.Command;
-import org.traccar.Protocol;
import java.util.Date;
@@ -59,8 +60,9 @@ public class H02ProtocolEncoder extends StringProtocolEncoder {
return formatCommand(time, uniqueId, "S20", "1", "0");
case Command.TYPE_POSITION_PERIODIC:
String frequency = command.getAttributes().get(Command.KEY_FREQUENCY).toString();
- if (Context.getIdentityManager().lookupAttributeBoolean(
- command.getDeviceId(), getProtocolName() + ".alternative", false, false, true)) {
+ if (AttributeUtil.lookup(
+ getCacheManager(), Keys.PROTOCOL_ALTERNATIVE.withPrefix(getProtocolName()),
+ command.getDeviceId())) {
return formatCommand(time, uniqueId, "D1", frequency);
} else {
return formatCommand(time, uniqueId, "S71", "22", frequency);
diff --git a/src/main/java/org/traccar/protocol/HaicomProtocol.java b/src/main/java/org/traccar/protocol/HaicomProtocol.java
index 6e5760bd4..f56c605f0 100644
--- a/src/main/java/org/traccar/protocol/HaicomProtocol.java
+++ b/src/main/java/org/traccar/protocol/HaicomProtocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class HaicomProtocol extends BaseProtocol {
- public HaicomProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public HaicomProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '*'));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/HaicomProtocolDecoder.java b/src/main/java/org/traccar/protocol/HaicomProtocolDecoder.java
index dd20f2aeb..9903e7735 100644
--- a/src/main/java/org/traccar/protocol/HaicomProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/HaicomProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/HomtecsProtocol.java b/src/main/java/org/traccar/protocol/HomtecsProtocol.java
index 34dbf0f51..aa2d7d852 100644
--- a/src/main/java/org/traccar/protocol/HomtecsProtocol.java
+++ b/src/main/java/org/traccar/protocol/HomtecsProtocol.java
@@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class HomtecsProtocol extends BaseProtocol {
- public HomtecsProtocol() {
- addServer(new TrackerServer(true, getName()) {
+ @Inject
+ public HomtecsProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new HomtecsProtocolDecoder(HomtecsProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/HomtecsProtocolDecoder.java b/src/main/java/org/traccar/protocol/HomtecsProtocolDecoder.java
index a93572b5c..5541cb065 100644
--- a/src/main/java/org/traccar/protocol/HomtecsProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/HomtecsProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/HoopoProtocol.java b/src/main/java/org/traccar/protocol/HoopoProtocol.java
index 387b967d3..02d8e5a8e 100644
--- a/src/main/java/org/traccar/protocol/HoopoProtocol.java
+++ b/src/main/java/org/traccar/protocol/HoopoProtocol.java
@@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class HoopoProtocol extends BaseProtocol {
- public HoopoProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public HoopoProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new JsonFrameDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/HoopoProtocolDecoder.java b/src/main/java/org/traccar/protocol/HoopoProtocolDecoder.java
index 5db7f0bc0..708c74f2a 100644
--- a/src/main/java/org/traccar/protocol/HoopoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/HoopoProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.model.Position;
@@ -52,7 +52,7 @@ public class HoopoProtocolDecoder extends BaseProtocolDecoder {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- Date time = new Date(OffsetDateTime.parse(eventData.getString("receiveTime")).toInstant().toEpochMilli());
+ Date time = new Date(OffsetDateTime.parse(json.getString("eventTime")).toInstant().toEpochMilli());
position.setTime(time);
position.setValid(true);
@@ -62,6 +62,10 @@ public class HoopoProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_EVENT, eventData.getString("eventType"));
position.set(Position.KEY_BATTERY_LEVEL, eventData.getInt("batteryLevel"));
+ if (json.containsKey("movement")) {
+ position.setSpeed(json.getJsonObject("movement").getInt("Speed"));
+ }
+
return position;
}
diff --git a/src/main/java/org/traccar/protocol/HuaShengProtocol.java b/src/main/java/org/traccar/protocol/HuaShengProtocol.java
index 103f2d501..b1b61e977 100644
--- a/src/main/java/org/traccar/protocol/HuaShengProtocol.java
+++ b/src/main/java/org/traccar/protocol/HuaShengProtocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class HuaShengProtocol extends BaseProtocol {
- public HuaShengProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public HuaShengProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new HuaShengFrameDecoder());
pipeline.addLast(new HuaShengProtocolDecoder(HuaShengProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java
index 891046213..371691d82 100644
--- a/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java
@@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocol.java b/src/main/java/org/traccar/protocol/HuabaoProtocol.java
index 791672b85..c37918b0e 100644
--- a/src/main/java/org/traccar/protocol/HuabaoProtocol.java
+++ b/src/main/java/org/traccar/protocol/HuabaoProtocol.java
@@ -18,17 +18,21 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class HuabaoProtocol extends BaseProtocol {
- public HuabaoProtocol() {
+ @Inject
+ public HuabaoProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_ENGINE_STOP,
Command.TYPE_ENGINE_RESUME);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new HuabaoFrameDecoder());
pipeline.addLast(new HuabaoProtocolEncoder(HuabaoProtocol.this));
pipeline.addLast(new HuabaoProtocolDecoder(HuabaoProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
index 871410c44..d0bbeebb5 100644
--- a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BcdUtil;
@@ -34,7 +34,10 @@ import org.traccar.model.Position;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
import java.util.Calendar;
+import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.TimeZone;
@@ -48,11 +51,13 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_GENERAL_RESPONSE = 0x8001;
public static final int MSG_GENERAL_RESPONSE_2 = 0x4401;
public static final int MSG_HEARTBEAT = 0x0002;
+ public static final int MSG_HEARTBEAT_2 = 0x0506;
public static final int MSG_TERMINAL_REGISTER = 0x0100;
public static final int MSG_TERMINAL_REGISTER_RESPONSE = 0x8100;
public static final int MSG_TERMINAL_CONTROL = 0x8105;
public static final int MSG_TERMINAL_AUTH = 0x0102;
public static final int MSG_LOCATION_REPORT = 0x0200;
+ public static final int MSG_LOCATION_BATCH_2 = 0x0210;
public static final int MSG_ACCELERATION = 0x2070;
public static final int MSG_LOCATION_REPORT_2 = 0x5501;
public static final int MSG_LOCATION_REPORT_BLIND = 0x5502;
@@ -61,6 +66,7 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_TIME_SYNC_REQUEST = 0x0109;
public static final int MSG_TIME_SYNC_RESPONSE = 0x8109;
public static final int MSG_PHOTO = 0x8888;
+ public static final int MSG_TRANSPARENT = 0x0900;
public static final int RESULT_SUCCESS = 0;
@@ -119,6 +125,9 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
|| BitUtil.check(value, 10) || BitUtil.check(value, 11)) {
return Position.ALARM_FAULT;
}
+ if (BitUtil.check(value, 7) || BitUtil.check(value, 18)) {
+ return Position.ALARM_LOW_BATTERY;
+ }
if (BitUtil.check(value, 8)) {
return Position.ALARM_POWER_OFF;
}
@@ -142,6 +151,28 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
return BitUtil.check(value, 15) ? -BitUtil.to(value, 15) : BitUtil.to(value, 15);
}
+ private Date readDate(ByteBuf buf, TimeZone timeZone) {
+ DateBuilder dateBuilder = new DateBuilder(timeZone)
+ .setYear(BcdUtil.readInteger(buf, 2))
+ .setMonth(BcdUtil.readInteger(buf, 2))
+ .setDay(BcdUtil.readInteger(buf, 2))
+ .setHour(BcdUtil.readInteger(buf, 2))
+ .setMinute(BcdUtil.readInteger(buf, 2))
+ .setSecond(BcdUtil.readInteger(buf, 2));
+ return dateBuilder.getDate();
+ }
+
+ private String decodeId(ByteBuf id) {
+ String serial = ByteBufUtil.hexDump(id);
+ if (serial.matches("[0-9]+")) {
+ return serial;
+ } else {
+ long imei = id.getUnsignedShort(0);
+ imei = (imei << 32) + id.getUnsignedInt(2);
+ return String.valueOf(imei);
+ }
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -149,7 +180,19 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
ByteBuf buf = (ByteBuf) msg;
if (buf.getByte(buf.readerIndex()) == '(') {
- return decodeResult(channel, remoteAddress, buf.toString(StandardCharsets.US_ASCII));
+ String sentence = buf.toString(StandardCharsets.US_ASCII);
+ if (sentence.contains("BASE,2")) {
+ DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ String response = sentence.replace("TIME", dateFormat.format(new Date()));
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage(
+ Unpooled.copiedBuffer(response, StandardCharsets.US_ASCII), remoteAddress));
+ }
+ return null;
+ } else {
+ return decodeResult(channel, remoteAddress, sentence);
+ }
}
buf.readUnsignedByte(); // start marker
@@ -163,13 +206,13 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
index = buf.readUnsignedShort();
}
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, ByteBufUtil.hexDump(id));
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, decodeId(id));
if (deviceSession == null) {
return null;
}
- if (deviceSession.getTimeZone() == null) {
- deviceSession.setTimeZone(getTimeZone(deviceSession.getDeviceId(), "GMT+8"));
+ if (!deviceSession.contains(DeviceSession.KEY_TIMEZONE)) {
+ deviceSession.set(DeviceSession.KEY_TIMEZONE, getTimeZone(deviceSession.getDeviceId(), "GMT+8"));
}
if (type == MSG_TERMINAL_REGISTER) {
@@ -178,12 +221,12 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
ByteBuf response = Unpooled.buffer();
response.writeShort(index);
response.writeByte(RESULT_SUCCESS);
- response.writeBytes(ByteBufUtil.hexDump(id).getBytes(StandardCharsets.US_ASCII));
+ response.writeBytes(decodeId(id).getBytes(StandardCharsets.US_ASCII));
channel.writeAndFlush(new NetworkMessage(
formatMessage(MSG_TERMINAL_REGISTER_RESPONSE, id, false, response), remoteAddress));
}
- } else if (type == MSG_TERMINAL_AUTH || type == MSG_HEARTBEAT || type == MSG_PHOTO) {
+ } else if (type == MSG_TERMINAL_AUTH || type == MSG_HEARTBEAT || type == MSG_HEARTBEAT_2 || type == MSG_PHOTO) {
sendGeneralResponse(channel, remoteAddress, id, type, index);
@@ -201,11 +244,11 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
return decodeLocation2(deviceSession, buf, type);
- } else if (type == MSG_LOCATION_BATCH) {
+ } else if (type == MSG_LOCATION_BATCH || type == MSG_LOCATION_BATCH_2) {
sendGeneralResponse(channel, remoteAddress, id, type, index);
- return decodeLocationBatch(deviceSession, buf);
+ return decodeLocationBatch(deviceSession, buf, type);
} else if (type == MSG_TIME_SYNC_REQUEST) {
@@ -249,6 +292,10 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
return position;
+ } else if (type == MSG_TRANSPARENT) {
+
+ return decodeTransparent(deviceSession, buf);
+
}
return null;
@@ -266,17 +313,83 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- private Position decodeLocation(DeviceSession deviceSession, ByteBuf buf) {
-
- Position position = new Position(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
+ private void decodeExtension(Position position, ByteBuf buf, int endIndex) {
+ while (buf.readerIndex() < endIndex) {
+ int type = buf.readUnsignedByte();
+ int length = buf.readUnsignedByte();
+ switch (type) {
+ case 0x01:
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 100L);
+ break;
+ case 0x02:
+ position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedShort() * 0.1);
+ break;
+ case 0x03:
+ position.set(Position.KEY_OBD_SPEED, buf.readUnsignedShort() * 0.1);
+ break;
+ case 0x80:
+ position.set(Position.KEY_OBD_SPEED, buf.readUnsignedByte());
+ break;
+ case 0x81:
+ position.set(Position.KEY_RPM, buf.readUnsignedShort());
+ break;
+ case 0x82:
+ position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.1);
+ break;
+ case 0x83:
+ position.set(Position.KEY_ENGINE_LOAD, buf.readUnsignedByte());
+ break;
+ case 0x84:
+ position.set(Position.KEY_COOLANT_TEMP, buf.readUnsignedByte() - 40);
+ break;
+ case 0x85:
+ position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedShort());
+ break;
+ case 0x86:
+ position.set("intakeTemp", buf.readUnsignedByte() - 40);
+ break;
+ case 0x87:
+ position.set("intakeFlow", buf.readUnsignedShort());
+ break;
+ case 0x88:
+ position.set("intakePressure", buf.readUnsignedByte());
+ break;
+ case 0x89:
+ position.set(Position.KEY_THROTTLE, buf.readUnsignedByte());
+ break;
+ case 0x8B:
+ position.set(Position.KEY_VIN, buf.readCharSequence(17, StandardCharsets.US_ASCII).toString());
+ break;
+ case 0x8C:
+ position.set(Position.KEY_OBD_ODOMETER, buf.readUnsignedInt() * 100L);
+ break;
+ case 0x8D:
+ position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedShort() * 1000L);
+ break;
+ case 0x8E:
+ position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedByte());
+ break;
+ case 0xA0:
+ String codes = buf.readCharSequence(length, StandardCharsets.US_ASCII).toString();
+ position.set(Position.KEY_DTCS, codes.replace(',', ' '));
+ break;
+ case 0xCC:
+ position.set(Position.KEY_ICCID, buf.readCharSequence(20, StandardCharsets.US_ASCII).toString());
+ break;
+ default:
+ buf.skipBytes(length);
+ break;
+ }
+ }
+ }
- position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedInt()));
+ private void decodeCoordinates(Position position, ByteBuf buf) {
int status = buf.readInt();
position.set(Position.KEY_IGNITION, BitUtil.check(status, 0));
position.set(Position.KEY_BLOCKED, BitUtil.check(status, 10));
+ position.set(Position.KEY_CHARGE, BitUtil.check(status, 26));
position.setValid(BitUtil.check(status, 1));
@@ -294,19 +407,28 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
} else {
position.setLongitude(lon);
}
+ }
+
+ private double decodeCustomDouble(ByteBuf buf) {
+ int b1 = buf.readByte();
+ int b2 = buf.readUnsignedByte();
+ int sign = b1 != 0 ? b1 / Math.abs(b1) : 1;
+ return sign * (Math.abs(b1) + b2 / 255.0);
+ }
+
+ private Position decodeLocation(DeviceSession deviceSession, ByteBuf buf) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedInt()));
+
+ decodeCoordinates(position, buf);
position.setAltitude(buf.readShort());
position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort() * 0.1));
position.setCourse(buf.readUnsignedShort());
-
- DateBuilder dateBuilder = new DateBuilder(deviceSession.getTimeZone())
- .setYear(BcdUtil.readInteger(buf, 2))
- .setMonth(BcdUtil.readInteger(buf, 2))
- .setDay(BcdUtil.readInteger(buf, 2))
- .setHour(BcdUtil.readInteger(buf, 2))
- .setMinute(BcdUtil.readInteger(buf, 2))
- .setSecond(BcdUtil.readInteger(buf, 2));
- position.setTime(dateBuilder.getDate());
+ position.setTime(readDate(buf, deviceSession.get(DeviceSession.KEY_TIMEZONE)));
if (buf.readableBytes() == 20) {
@@ -333,6 +455,9 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
case 0x02:
position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedShort() * 0.1);
break;
+ case 0x2b:
+ position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedInt());
+ break;
case 0x30:
position.set(Position.KEY_RSSI, buf.readUnsignedByte());
break;
@@ -346,6 +471,11 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_BATTERY, Integer.parseInt(lockStatus.substring(2, 5)) * 0.01);
}
break;
+ case 0x80:
+ buf.readUnsignedByte(); // content
+ endIndex = buf.writerIndex() - 2;
+ decodeExtension(position, buf, endIndex);
+ break;
case 0x91:
position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.1);
position.set(Position.KEY_RPM, buf.readUnsignedShort());
@@ -383,11 +513,21 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.1);
break;
case 0xD4:
- case 0xFE:
position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
break;
case 0xD5:
- position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.01);
+ if (length == 2) {
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.01);
+ } else {
+ int count = buf.readUnsignedByte();
+ for (int i = 1; i <= count; i++) {
+ position.set("lock" + i + "Id", ByteBufUtil.hexDump(buf.readSlice(5)));
+ position.set("lock" + i + "Card", ByteBufUtil.hexDump(buf.readSlice(5)));
+ position.set("lock" + i + "Battery", buf.readUnsignedByte());
+ int status = buf.readUnsignedShort();
+ position.set("lock" + i + "Locked", !BitUtil.check(status, 5));
+ }
+ }
break;
case 0xDA:
buf.readUnsignedShort(); // string cut count
@@ -396,6 +536,14 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_MOTION, BitUtil.check(deviceStatus, 2));
position.set("cover", BitUtil.check(deviceStatus, 3));
break;
+ case 0xE6:
+ while (buf.readerIndex() < endIndex) {
+ int sensorIndex = buf.readUnsignedByte();
+ buf.skipBytes(6); // mac
+ position.set(Position.PREFIX_TEMP + sensorIndex, decodeCustomDouble(buf));
+ position.set("humidity" + sensorIndex, decodeCustomDouble(buf));
+ }
+ break;
case 0xEB:
if (buf.getUnsignedShort(buf.readerIndex()) > 200) {
Network network = new Network();
@@ -423,6 +571,16 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
case 0x00CE:
position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01);
break;
+ case 0x00D8:
+ Network network = new Network();
+ network.addCellTower(CellTower.from(
+ buf.readUnsignedShort(), buf.readUnsignedByte(),
+ buf.readUnsignedShort(), buf.readUnsignedInt()));
+ position.setNetwork(network);
+ break;
+ case 0xE1:
+ position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
+ break;
default:
buf.skipBytes(extendedLength - 2);
break;
@@ -440,6 +598,105 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001);
position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
break;
+ case 0xF3:
+ while (buf.readerIndex() < endIndex) {
+ int extendedType = buf.readUnsignedShort();
+ int extendedLength = buf.readUnsignedByte();
+ switch (extendedType) {
+ case 0x0002:
+ position.set(Position.KEY_OBD_SPEED, buf.readUnsignedShort() * 0.1);
+ break;
+ case 0x0003:
+ position.set(Position.KEY_RPM, buf.readUnsignedShort());
+ break;
+ case 0x0004:
+ position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.001);
+ break;
+ case 0x0005:
+ position.set(Position.KEY_OBD_ODOMETER, buf.readUnsignedInt() * 100);
+ break;
+ case 0x0007:
+ position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedShort() * 0.1);
+ break;
+ case 0x0008:
+ position.set(Position.KEY_ENGINE_LOAD, buf.readUnsignedShort() * 0.1);
+ break;
+ case 0x0009:
+ position.set(Position.KEY_COOLANT_TEMP, buf.readUnsignedShort() - 40);
+ break;
+ case 0x000B:
+ position.set("intakePressure", buf.readUnsignedShort());
+ break;
+ case 0x000C:
+ position.set("intakeTemp", buf.readUnsignedShort() - 40);
+ break;
+ case 0x000D:
+ position.set("intakeFlow", buf.readUnsignedShort());
+ break;
+ case 0x000E:
+ position.set(Position.KEY_THROTTLE, buf.readUnsignedShort() * 100 / 255);
+ break;
+ case 0x0050:
+ position.set(Position.KEY_VIN, buf.readSlice(17).toString(StandardCharsets.US_ASCII));
+ break;
+ case 0x0100:
+ position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedShort() * 0.1);
+ break;
+ case 0x0102:
+ position.set("tripFuel", buf.readUnsignedShort() * 0.1);
+ break;
+ case 0x0112:
+ position.set("hardAccelerationCount", buf.readUnsignedShort());
+ break;
+ case 0x0113:
+ position.set("hardDecelerationCount", buf.readUnsignedShort());
+ break;
+ case 0x0114:
+ position.set("hardCorneringCount", buf.readUnsignedShort());
+ break;
+ default:
+ buf.skipBytes(extendedLength);
+ break;
+ }
+ }
+ break;
+ case 0xFE:
+ if (length == 1) {
+ position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
+ } else {
+ int mark = buf.readUnsignedByte();
+ if (mark == 0x7C) {
+ while (buf.readerIndex() < endIndex) {
+ int extendedType = buf.readUnsignedByte();
+ int extendedLength = buf.readUnsignedByte();
+ switch (extendedType) {
+ case 0x01:
+ long alarms = buf.readUnsignedInt();
+ if (BitUtil.check(alarms, 0)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
+ }
+ if (BitUtil.check(alarms, 1)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
+ }
+ if (BitUtil.check(alarms, 2)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_CORNERING);
+ }
+ if (BitUtil.check(alarms, 3)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCIDENT);
+ }
+ if (BitUtil.check(alarms, 4)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_TAMPERING);
+ }
+ break;
+ default:
+ buf.skipBytes(extendedLength);
+ break;
+ }
+ }
+ }
+ position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
+ }
+ break;
default:
break;
}
@@ -468,7 +725,8 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_CHARGE, true);
}
- position.setNetwork(new Network(CellTower.fromCidLac(buf.readUnsignedInt(), buf.readUnsignedShort())));
+ position.setNetwork(new Network(CellTower.fromCidLac(
+ getConfig(), buf.readUnsignedInt(), buf.readUnsignedShort())));
int product = buf.readUnsignedByte();
int status = buf.readUnsignedShort();
@@ -496,24 +754,141 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
return position;
}
- private List<Position> decodeLocationBatch(DeviceSession deviceSession, ByteBuf buf) {
+ private List<Position> decodeLocationBatch(DeviceSession deviceSession, ByteBuf buf, int type) {
List<Position> positions = new LinkedList<>();
- int count = buf.readUnsignedShort();
- int locationType = buf.readUnsignedByte();
+ int locationType = 0;
+ if (type == MSG_LOCATION_BATCH) {
+ buf.readUnsignedShort(); // count
+ locationType = buf.readUnsignedByte();
+ }
- for (int i = 0; i < count; i++) {
- int endIndex = buf.readUnsignedShort() + buf.readerIndex();
- Position position = decodeLocation(deviceSession, buf);
- if (locationType == 0) {
+ while (buf.readableBytes() > 2) {
+ int length = type == MSG_LOCATION_BATCH_2 ? buf.readUnsignedByte() : buf.readUnsignedShort();
+ ByteBuf fragment = buf.readSlice(length);
+ Position position = decodeLocation(deviceSession, fragment);
+ if (locationType > 0) {
position.set(Position.KEY_ARCHIVE, true);
}
positions.add(position);
- buf.readerIndex(endIndex);
}
return positions;
}
+ private Position decodeTransparent(DeviceSession deviceSession, ByteBuf buf) {
+
+ int type = buf.readUnsignedByte();
+
+ if (type == 0xF0) {
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ Date time = readDate(buf, deviceSession.get(DeviceSession.KEY_TIMEZONE));
+
+ if (buf.readUnsignedByte() > 0) {
+ position.set(Position.KEY_ARCHIVE, true);
+ }
+
+ buf.readUnsignedByte(); // vehicle type
+
+ int count;
+ int subtype = buf.readUnsignedByte();
+ switch (subtype) {
+ case 0x01:
+ count = buf.readUnsignedByte();
+ for (int i = 0; i < count; i++) {
+ int id = buf.readUnsignedShort();
+ int length = buf.readUnsignedByte();
+ switch (id) {
+ case 0x0102:
+ case 0x0528:
+ case 0x0546:
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 100);
+ break;
+ case 0x0103:
+ position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedInt() * 0.01);
+ break;
+ case 0x052A:
+ position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedShort() * 0.01);
+ break;
+ case 0x0105:
+ case 0x052C:
+ position.set(Position.KEY_FUEL_USED, buf.readUnsignedInt() * 0.01);
+ break;
+ case 0x014A:
+ case 0x0537:
+ case 0x0538:
+ case 0x0539:
+ position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedShort() * 0.01);
+ break;
+ default:
+ switch (length) {
+ case 1:
+ position.set(Position.PREFIX_IO + id, buf.readUnsignedByte());
+ break;
+ case 2:
+ position.set(Position.PREFIX_IO + id, buf.readUnsignedShort());
+ break;
+ case 4:
+ position.set(Position.PREFIX_IO + id, buf.readUnsignedInt());
+ break;
+ default:
+ buf.skipBytes(length);
+ break;
+ }
+ break;
+ }
+ }
+ decodeCoordinates(position, buf);
+ position.setTime(time);
+ break;
+ case 0x03:
+ count = buf.readUnsignedByte();
+ for (int i = 0; i < count; i++) {
+ int id = buf.readUnsignedShort();
+ int length = buf.readUnsignedByte();
+ switch (id) {
+ case 0x1A:
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
+ break;
+ case 0x1B:
+ position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
+ break;
+ case 0x1C:
+ position.set(Position.KEY_ALARM, Position.ALARM_CORNERING);
+ break;
+ case 0x1D:
+ case 0x1E:
+ case 0x1F:
+ position.set(Position.KEY_ALARM, Position.ALARM_LANE_CHANGE);
+ break;
+ case 0x23:
+ position.set(Position.KEY_ALARM, Position.ALARM_FATIGUE_DRIVING);
+ break;
+ default:
+ break;
+ }
+ buf.skipBytes(length);
+ }
+ decodeCoordinates(position, buf);
+ position.setTime(time);
+ break;
+ case 0x0B:
+ if (buf.readUnsignedByte() > 0) {
+ position.set(Position.KEY_VIN, buf.readCharSequence(17, StandardCharsets.US_ASCII).toString());
+ }
+ getLastLocation(position, time);
+ break;
+ default:
+ return null;
+ }
+
+ return position;
+ }
+
+ return null;
+ }
+
}
diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocolEncoder.java b/src/main/java/org/traccar/protocol/HuabaoProtocolEncoder.java
index 55c1e0c3b..ada7e3fba 100644
--- a/src/main/java/org/traccar/protocol/HuabaoProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/HuabaoProtocolEncoder.java
@@ -18,10 +18,11 @@ package org.traccar.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.traccar.BaseProtocolEncoder;
-import org.traccar.Context;
+import org.traccar.Protocol;
+import org.traccar.config.Keys;
import org.traccar.helper.DataConverter;
+import org.traccar.helper.model.AttributeUtil;
import org.traccar.model.Command;
-import org.traccar.Protocol;
import java.text.SimpleDateFormat;
import java.util.Date;
@@ -35,8 +36,8 @@ public class HuabaoProtocolEncoder extends BaseProtocolEncoder {
@Override
protected Object encodeCommand(Command command) {
- boolean alternative = Context.getIdentityManager().lookupAttributeBoolean(
- command.getDeviceId(), getProtocolName() + ".alternative", false, false, true);
+ boolean alternative = AttributeUtil.lookup(
+ getCacheManager(), Keys.PROTOCOL_ALTERNATIVE.withPrefix(getProtocolName()), command.getDeviceId());
ByteBuf id = Unpooled.wrappedBuffer(
DataConverter.parseHex(getUniqueId(command.getDeviceId())));
diff --git a/src/main/java/org/traccar/protocol/HunterProProtocol.java b/src/main/java/org/traccar/protocol/HunterProProtocol.java
index 9f6424a57..ed4289d73 100644
--- a/src/main/java/org/traccar/protocol/HunterProProtocol.java
+++ b/src/main/java/org/traccar/protocol/HunterProProtocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class HunterProProtocol extends BaseProtocol {
- public HunterProProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public HunterProProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "\r"));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/HunterProProtocolDecoder.java b/src/main/java/org/traccar/protocol/HunterProProtocolDecoder.java
index 06bc12d59..eada1fd9a 100644
--- a/src/main/java/org/traccar/protocol/HunterProProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/HunterProProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/IdplProtocol.java b/src/main/java/org/traccar/protocol/IdplProtocol.java
index 418178756..aa1f4ff5b 100644
--- a/src/main/java/org/traccar/protocol/IdplProtocol.java
+++ b/src/main/java/org/traccar/protocol/IdplProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class IdplProtocol extends BaseProtocol {
- public IdplProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public IdplProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/IdplProtocolDecoder.java b/src/main/java/org/traccar/protocol/IdplProtocolDecoder.java
index cf3c03d7f..72409b168 100644
--- a/src/main/java/org/traccar/protocol/IdplProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/IdplProtocolDecoder.java
@@ -20,7 +20,7 @@ import java.util.regex.Pattern;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.Parser.CoordinateFormat;
diff --git a/src/main/java/org/traccar/protocol/IntellitracProtocol.java b/src/main/java/org/traccar/protocol/IntellitracProtocol.java
index 3abf40da7..b1a91cca9 100644
--- a/src/main/java/org/traccar/protocol/IntellitracProtocol.java
+++ b/src/main/java/org/traccar/protocol/IntellitracProtocol.java
@@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class IntellitracProtocol extends BaseProtocol {
- public IntellitracProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public IntellitracProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new IntellitracFrameDecoder(1024));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/IntellitracProtocolDecoder.java b/src/main/java/org/traccar/protocol/IntellitracProtocolDecoder.java
index 930d4f23b..b86584016 100644
--- a/src/main/java/org/traccar/protocol/IntellitracProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/IntellitracProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/IotmProtocol.java b/src/main/java/org/traccar/protocol/IotmProtocol.java
index f202d9b9d..0d288f4bf 100644
--- a/src/main/java/org/traccar/protocol/IotmProtocol.java
+++ b/src/main/java/org/traccar/protocol/IotmProtocol.java
@@ -20,13 +20,17 @@ import io.netty.handler.codec.mqtt.MqttEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class IotmProtocol extends BaseProtocol {
- public IotmProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public IotmProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(MqttEncoder.INSTANCE);
pipeline.addLast(new MqttDecoder());
pipeline.addLast(new IotmProtocolDecoder(IotmProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/IotmProtocolDecoder.java b/src/main/java/org/traccar/protocol/IotmProtocolDecoder.java
index 9c94ffd4b..7bbe6c8de 100644
--- a/src/main/java/org/traccar/protocol/IotmProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/IotmProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2020 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,7 +25,7 @@ import io.netty.handler.codec.mqtt.MqttMessageBuilders;
import io.netty.handler.codec.mqtt.MqttPublishMessage;
import io.netty.handler.codec.mqtt.MqttSubscribeMessage;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.UnitsConverter;
@@ -260,7 +260,7 @@ public class IotmProtocolDecoder extends BaseProtocolDecoder {
MqttSubscribeMessage message = (MqttSubscribeMessage) msg;
MqttMessage response = MqttMessageBuilders.subAck()
- .packetId((short) message.variableHeader().messageId())
+ .packetId(message.variableHeader().messageId())
.build();
if (channel != null) {
@@ -339,7 +339,7 @@ public class IotmProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedByte(); // checksum
MqttMessage response = MqttMessageBuilders.pubAck()
- .packetId((short) message.variableHeader().packetId())
+ .packetId(message.variableHeader().packetId())
.build();
if (channel != null) {
diff --git a/src/main/java/org/traccar/protocol/ItsProtocol.java b/src/main/java/org/traccar/protocol/ItsProtocol.java
index 45df3da11..5148e8ab0 100644
--- a/src/main/java/org/traccar/protocol/ItsProtocol.java
+++ b/src/main/java/org/traccar/protocol/ItsProtocol.java
@@ -20,17 +20,21 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class ItsProtocol extends BaseProtocol {
- public ItsProtocol() {
+ @Inject
+ public ItsProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_ENGINE_STOP,
Command.TYPE_ENGINE_RESUME);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new ItsFrameDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java b/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java
index 9eed58347..8a8d734cf 100644
--- a/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
@@ -205,7 +205,8 @@ public class ItsProtocolDecoder extends BaseProtocolDecoder {
if (parser.hasNext()) {
position.setValid(parser.nextInt() == 1);
}
- position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+ position.setTime(parser.nextDateTime(
+ Parser.DateTimeFormat.DMY_HMS, getTimeZone(deviceSession.getDeviceId()).getID()));
if (parser.hasNext()) {
position.setValid(parser.next().matches("[1A]"));
}
diff --git a/src/main/java/org/traccar/protocol/Ivt401Protocol.java b/src/main/java/org/traccar/protocol/Ivt401Protocol.java
index fb44e4fe9..763457641 100644
--- a/src/main/java/org/traccar/protocol/Ivt401Protocol.java
+++ b/src/main/java/org/traccar/protocol/Ivt401Protocol.java
@@ -20,13 +20,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class Ivt401Protocol extends BaseProtocol {
- public Ivt401Protocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public Ivt401Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ';'));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new Ivt401ProtocolDecoder(Ivt401Protocol.this));
diff --git a/src/main/java/org/traccar/protocol/Ivt401ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Ivt401ProtocolDecoder.java
index 63556e7a9..972f22ebe 100644
--- a/src/main/java/org/traccar/protocol/Ivt401ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Ivt401ProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/JidoProtocol.java b/src/main/java/org/traccar/protocol/JidoProtocol.java
new file mode 100644
index 000000000..78aa6c81c
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/JidoProtocol.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.CharacterDelimiterFrameDecoder;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
+
+public class JidoProtocol extends BaseProtocol {
+
+ @Inject
+ public JidoProtocol(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 JidoProtocolDecoder(JidoProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/JidoProtocolDecoder.java b/src/main/java/org/traccar/protocol/JidoProtocolDecoder.java
new file mode 100644
index 000000000..98fb36e11
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/JidoProtocolDecoder.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2021 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.session.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.regex.Pattern;
+
+public class JidoProtocolDecoder extends BaseProtocolDecoder {
+
+ public JidoProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("*")
+ .number("(d+),") // imei
+ .number("(d+),") // command
+ .expression("([AV]),").optional() // validity
+ .number("(dd)(dd)(dd),") // date (ddmmyy)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(d+)(dd.d+),") // latitude
+ .expression("([NS]),")
+ .number("(d+)(dd.d+),") // longitude
+ .expression("([EW]),")
+ .groupBegin()
+ .number("(d+),") // speed
+ .number("(d+),") // odometer
+ .number("(d+),") // course
+ .number("(-?d+),") // altitude
+ .number("(d+),") // satellites
+ .number("d+,") // gsm 1
+ .number("d+,") // gsm 2
+ .number("([01]),") // charging
+ .number("(d+),") // battery level
+ .expression("([YKN]),") // mode
+ .number("([01]),") // lock
+ .number("[^,]+,") // accelerometer x
+ .number("[^,]+,") // accelerometer y
+ .number("[^,]+,") // accelerometer z
+ .or()
+ .expression("[^,]*,") // data
+ .groupEnd()
+ .number("xx") // checksum
+ .compile();
+
+ private String decodeAlarm(int type) {
+ switch (type) {
+ case 3:
+ return Position.ALARM_LOW_BATTERY;
+ case 4:
+ return Position.ALARM_TAMPERING;
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ Parser parser = new Parser(PATTERN, (String) msg);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.set(Position.KEY_ALARM, decodeAlarm(parser.nextInt()));
+
+ if (parser.hasNext()) {
+ position.setValid(parser.next().equals("A"));
+ } else {
+ position.setValid(true);
+ }
+
+ position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+ position.setLatitude(parser.nextCoordinate());
+ position.setLongitude(parser.nextCoordinate());
+
+ if (parser.hasNext(9)) {
+
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextInt()));
+
+ position.set(Position.KEY_ODOMETER, parser.nextInt());
+
+ position.setCourse(parser.nextInt());
+ position.setAltitude(parser.nextInt());
+
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_CHARGE, parser.nextInt() > 0);
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
+ position.set("mode", parser.next());
+ position.set(Position.KEY_BLOCKED, parser.nextInt() > 0);
+
+ }
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/JpKorjarProtocol.java b/src/main/java/org/traccar/protocol/JpKorjarProtocol.java
index fe5b2480d..30c8e9977 100644
--- a/src/main/java/org/traccar/protocol/JpKorjarProtocol.java
+++ b/src/main/java/org/traccar/protocol/JpKorjarProtocol.java
@@ -1,6 +1,6 @@
/*
+ * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2016 Nyash (nyashh@gmail.com)
- * Copyright 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class JpKorjarProtocol extends BaseProtocol {
- public JpKorjarProtocol() {
- addServer(new TrackerServer(false, this.getName()) {
+ @Inject
+ public JpKorjarProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new JpKorjarFrameDecoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new JpKorjarProtocolDecoder(JpKorjarProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/JpKorjarProtocolDecoder.java b/src/main/java/org/traccar/protocol/JpKorjarProtocolDecoder.java
index 33026918a..ffddcc568 100644
--- a/src/main/java/org/traccar/protocol/JpKorjarProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/JpKorjarProtocolDecoder.java
@@ -18,7 +18,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/Jt600Protocol.java b/src/main/java/org/traccar/protocol/Jt600Protocol.java
index 37c82f741..bf0b3379e 100644
--- a/src/main/java/org/traccar/protocol/Jt600Protocol.java
+++ b/src/main/java/org/traccar/protocol/Jt600Protocol.java
@@ -19,19 +19,23 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class Jt600Protocol extends BaseProtocol {
- public Jt600Protocol() {
+ @Inject
+ public Jt600Protocol(Config config) {
setSupportedDataCommands(
Command.TYPE_ENGINE_RESUME,
Command.TYPE_ENGINE_STOP,
Command.TYPE_SET_TIMEZONE,
Command.TYPE_REBOOT_DEVICE);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new Jt600FrameDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new Jt600ProtocolEncoder(Jt600Protocol.this));
diff --git a/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java
index 37c1674d4..9ed44f565 100644
--- a/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BcdUtil;
@@ -141,7 +141,9 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
int version = BitUtil.from(buf.readUnsignedByte(), 4);
buf.readUnsignedShort(); // length
- while (buf.readableBytes() > 1) {
+ boolean responseRequired = false;
+
+ while (buf.readableBytes() >= 17) {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
@@ -160,6 +162,9 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ALARM, BitUtil.check(status, 2) ? Position.ALARM_GEOFENCE_EXIT : null);
position.set(Position.KEY_ALARM, BitUtil.check(status, 3) ? Position.ALARM_POWER_CUT : null);
position.set(Position.KEY_ALARM, BitUtil.check(status, 4) ? Position.ALARM_VIBRATION : null);
+ if (BitUtil.check(status, 5)) {
+ responseRequired = true;
+ }
position.set(Position.KEY_BLOCKED, BitUtil.check(status, 7));
position.set(Position.KEY_ALARM, BitUtil.check(status, 8 + 3) ? Position.ALARM_LOW_BATTERY : null);
position.set(Position.KEY_ALARM, BitUtil.check(status, 8 + 6) ? Position.ALARM_FAULT : null);
@@ -172,7 +177,8 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_BATTERY_LEVEL, battery);
}
- CellTower cellTower = CellTower.fromCidLac(buf.readUnsignedShort(), buf.readUnsignedShort());
+ CellTower cellTower = CellTower.fromCidLac(
+ getConfig(), buf.readUnsignedShort(), buf.readUnsignedShort());
cellTower.setSignalStrength((int) buf.readUnsignedByte());
position.setNetwork(new Network(cellTower));
@@ -196,7 +202,7 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
int rssi = buf.readUnsignedByte();
if (cid != 0 && lac != 0) {
- CellTower cellTower = CellTower.fromCidLac(cid, lac);
+ CellTower cellTower = CellTower.fromCidLac(getConfig(), cid, lac);
cellTower.setSignalStrength(rssi);
position.setNetwork(new Network(cellTower));
} else {
@@ -232,7 +238,15 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
}
- buf.readUnsignedByte(); // index
+ int index = buf.readUnsignedByte();
+
+ if (channel != null && responseRequired) {
+ if (protocolVersion < 0x19) {
+ channel.writeAndFlush(new NetworkMessage("(P35)", remoteAddress));
+ } else {
+ channel.writeAndFlush(new NetworkMessage("(P69,0," + index + ")", remoteAddress));
+ }
+ }
return positions;
}
@@ -343,7 +357,7 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt(0));
position.set(Position.KEY_STATUS, parser.nextBinInt(0));
- CellTower cellTower = CellTower.fromCidLac(parser.nextInt(0), parser.nextInt(0));
+ CellTower cellTower = CellTower.fromCidLac(getConfig(), parser.nextInt(0), parser.nextInt(0));
cellTower.setSignalStrength(parser.nextInt(0));
position.setNetwork(new Network(cellTower));
@@ -361,6 +375,64 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private static final Pattern PATTERN_P45 = new PatternBuilder()
+ .text("(")
+ .number("(d+),") // id
+ .text("P45,") // type
+ .number("(dd)(dd)(dd),") // date (ddmmyy)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(d+.d+),([NS]),") // latitude
+ .number("(d+.d+),([EW]),") // longitude
+ .expression("([AV]),") // validity
+ .number("(d+),") // speed
+ .number("(d+),") // course
+ .number("d+,") // event source
+ .number("d+,") // unlock verification
+ .number("(d+),") // rfid
+ .number("d+,") // password verification
+ .number("d+,") // incorrect password count
+ .number("(d+),") // index
+ .any()
+ .compile();
+
+ private Position decodeP45(String sentence, Channel channel, SocketAddress remoteAddress) {
+
+ Parser parser = new Parser(PATTERN_P45, 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.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+
+ position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
+ position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
+ position.setValid(parser.next().equals("A"));
+
+ position.setSpeed(UnitsConverter.knotsFromMph(parser.nextDouble()));
+ position.setCourse(parser.nextDouble());
+
+ String rfid = parser.next();
+ if (!rfid.equals("0000000000")) {
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, rfid);
+ }
+
+ int index = parser.nextInt();
+
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage("(P69,0," + index + ")", remoteAddress));
+ }
+
+ return position;
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -374,6 +446,8 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
String sentence = buf.toString(StandardCharsets.US_ASCII);
if (sentence.contains("W01")) {
return decodeW01(sentence, channel, remoteAddress);
+ } else if (sentence.contains("P45")) {
+ return decodeP45(sentence, channel, remoteAddress);
} else {
return decodeU01(sentence, channel, remoteAddress);
}
diff --git a/src/main/java/org/traccar/protocol/KenjiProtocol.java b/src/main/java/org/traccar/protocol/KenjiProtocol.java
index 90c0c511c..8d78c8c56 100644
--- a/src/main/java/org/traccar/protocol/KenjiProtocol.java
+++ b/src/main/java/org/traccar/protocol/KenjiProtocol.java
@@ -22,13 +22,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class KenjiProtocol extends BaseProtocol {
- public KenjiProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public KenjiProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/KenjiProtocolDecoder.java b/src/main/java/org/traccar/protocol/KenjiProtocolDecoder.java
index 63812242a..fb989c72e 100644
--- a/src/main/java/org/traccar/protocol/KenjiProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/KenjiProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
diff --git a/src/main/java/org/traccar/protocol/KhdProtocol.java b/src/main/java/org/traccar/protocol/KhdProtocol.java
index 60a2aea7f..521274de5 100644
--- a/src/main/java/org/traccar/protocol/KhdProtocol.java
+++ b/src/main/java/org/traccar/protocol/KhdProtocol.java
@@ -19,11 +19,15 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class KhdProtocol extends BaseProtocol {
- public KhdProtocol() {
+ @Inject
+ public KhdProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_ENGINE_STOP,
Command.TYPE_ENGINE_RESUME,
@@ -33,9 +37,9 @@ public class KhdProtocol extends BaseProtocol {
Command.TYPE_SET_ODOMETER,
Command.TYPE_POSITION_SINGLE);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(512, 3, 2));
pipeline.addLast(new KhdProtocolEncoder(KhdProtocol.this));
pipeline.addLast(new KhdProtocolDecoder(KhdProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java b/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java
index a14f9b8a4..d7c236c4f 100644
--- a/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java
@@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BcdUtil;
diff --git a/src/main/java/org/traccar/protocol/KhdProtocolEncoder.java b/src/main/java/org/traccar/protocol/KhdProtocolEncoder.java
index 8aeb9660d..12353b415 100644
--- a/src/main/java/org/traccar/protocol/KhdProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/KhdProtocolEncoder.java
@@ -84,7 +84,7 @@ public class KhdProtocolEncoder extends BaseProtocolEncoder {
return encodeCommand(MSG_FACTORY_RESET, uniqueId, null);
case Command.TYPE_SET_SPEED_LIMIT:
ByteBuf content = Unpooled.buffer();
- content.writeByte(Integer.parseInt(command.getString(Command.KEY_DATA)));
+ content.writeByte(command.getInteger(Command.KEY_DATA));
return encodeCommand(MSG_RESUME_OIL, uniqueId, content);
case Command.TYPE_SET_ODOMETER:
return encodeCommand(MSG_DELETE_MILEAGE, uniqueId, null);
diff --git a/src/main/java/org/traccar/protocol/L100Protocol.java b/src/main/java/org/traccar/protocol/L100Protocol.java
index 942029307..0edea6095 100644
--- a/src/main/java/org/traccar/protocol/L100Protocol.java
+++ b/src/main/java/org/traccar/protocol/L100Protocol.java
@@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class L100Protocol extends BaseProtocol {
- public L100Protocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public L100Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new L100FrameDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/L100ProtocolDecoder.java b/src/main/java/org/traccar/protocol/L100ProtocolDecoder.java
index 5b5eb7d60..820de8f1c 100644
--- a/src/main/java/org/traccar/protocol/L100ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/L100ProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Checksum;
diff --git a/src/main/java/org/traccar/protocol/LacakProtocol.java b/src/main/java/org/traccar/protocol/LacakProtocol.java
index 0a0499ad7..bbebd51ed 100644
--- a/src/main/java/org/traccar/protocol/LacakProtocol.java
+++ b/src/main/java/org/traccar/protocol/LacakProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class LacakProtocol extends BaseProtocol {
- public LacakProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public LacakProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new HttpResponseEncoder());
pipeline.addLast(new HttpRequestDecoder());
pipeline.addLast(new HttpObjectAggregator(16384));
diff --git a/src/main/java/org/traccar/protocol/LacakProtocolDecoder.java b/src/main/java/org/traccar/protocol/LacakProtocolDecoder.java
index 132087c8f..809fafc90 100644
--- a/src/main/java/org/traccar/protocol/LacakProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/LacakProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.channel.Channel;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import org.traccar.BaseHttpProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.DateUtil;
import org.traccar.model.Position;
diff --git a/src/main/java/org/traccar/protocol/LaipacProtocol.java b/src/main/java/org/traccar/protocol/LaipacProtocol.java
index 1d561dbd2..249d3bcbe 100644
--- a/src/main/java/org/traccar/protocol/LaipacProtocol.java
+++ b/src/main/java/org/traccar/protocol/LaipacProtocol.java
@@ -18,23 +18,27 @@ package org.traccar.protocol;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
-import org.traccar.model.Command;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+import org.traccar.model.Command;
+
+import javax.inject.Inject;
public class LaipacProtocol extends BaseProtocol {
- public LaipacProtocol() {
+ @Inject
+ public LaipacProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_CUSTOM,
Command.TYPE_POSITION_SINGLE,
Command.TYPE_REBOOT_DEVICE);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java b/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java
index 45890e9a2..e9570ee11 100644
--- a/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java
@@ -17,8 +17,8 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
+import org.traccar.helper.model.AttributeUtil;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Checksum;
@@ -253,8 +253,8 @@ public class LaipacProtocolDecoder extends BaseProtocolDecoder {
sendAcknowledge(status, event, checksum, channel, remoteAddress);
- String devicePassword = Context.getIdentityManager()
- .getDevicePassword(deviceSession.getDeviceId(), getProtocolName(), DEFAULT_DEVICE_PASSWORD);
+ String devicePassword = AttributeUtil.getDevicePassword(
+ getCacheManager(), deviceSession.getDeviceId(), getProtocolName(), DEFAULT_DEVICE_PASSWORD);
sendEventResponse(event, devicePassword, channel, remoteAddress);
}
diff --git a/src/main/java/org/traccar/protocol/LeafSpyProtocol.java b/src/main/java/org/traccar/protocol/LeafSpyProtocol.java
index 05f63a2d7..7e13e23d0 100644
--- a/src/main/java/org/traccar/protocol/LeafSpyProtocol.java
+++ b/src/main/java/org/traccar/protocol/LeafSpyProtocol.java
@@ -22,13 +22,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class LeafSpyProtocol extends BaseProtocol {
- public LeafSpyProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public LeafSpyProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new HttpResponseEncoder());
pipeline.addLast(new HttpRequestDecoder());
pipeline.addLast(new HttpObjectAggregator(16384));
diff --git a/src/main/java/org/traccar/protocol/LeafSpyProtocolDecoder.java b/src/main/java/org/traccar/protocol/LeafSpyProtocolDecoder.java
index ad0c9bd32..6affb85c5 100644
--- a/src/main/java/org/traccar/protocol/LeafSpyProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/LeafSpyProtocolDecoder.java
@@ -22,7 +22,7 @@ import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.QueryStringDecoder;
import org.traccar.BaseHttpProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.model.Position;
diff --git a/src/main/java/org/traccar/protocol/M2cProtocol.java b/src/main/java/org/traccar/protocol/M2cProtocol.java
index 9de8526c3..a23ea0f57 100644
--- a/src/main/java/org/traccar/protocol/M2cProtocol.java
+++ b/src/main/java/org/traccar/protocol/M2cProtocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class M2cProtocol extends BaseProtocol {
- public M2cProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public M2cProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(32 * 1024, ']'));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/M2cProtocolDecoder.java b/src/main/java/org/traccar/protocol/M2cProtocolDecoder.java
index 1460bb176..9415d0f07 100644
--- a/src/main/java/org/traccar/protocol/M2cProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/M2cProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/M2mProtocol.java b/src/main/java/org/traccar/protocol/M2mProtocol.java
index dda328a59..6809d800c 100644
--- a/src/main/java/org/traccar/protocol/M2mProtocol.java
+++ b/src/main/java/org/traccar/protocol/M2mProtocol.java
@@ -19,13 +19,17 @@ import io.netty.handler.codec.FixedLengthFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class M2mProtocol extends BaseProtocol {
- public M2mProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public M2mProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new FixedLengthFrameDecoder(23));
pipeline.addLast(new M2mProtocolDecoder(M2mProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/M2mProtocolDecoder.java b/src/main/java/org/traccar/protocol/M2mProtocolDecoder.java
index 21e4a2fd0..7eca93a59 100644
--- a/src/main/java/org/traccar/protocol/M2mProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/M2mProtocolDecoder.java
@@ -18,7 +18,7 @@ package org.traccar.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.model.Position;
diff --git a/src/main/java/org/traccar/protocol/MaestroProtocol.java b/src/main/java/org/traccar/protocol/MaestroProtocol.java
index 87453ce7d..38a67f9a4 100644
--- a/src/main/java/org/traccar/protocol/MaestroProtocol.java
+++ b/src/main/java/org/traccar/protocol/MaestroProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class MaestroProtocol extends BaseProtocol {
- public MaestroProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public MaestroProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new FixedLengthFrameDecoder(160));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/MaestroProtocolDecoder.java b/src/main/java/org/traccar/protocol/MaestroProtocolDecoder.java
index 37b097414..78308658e 100644
--- a/src/main/java/org/traccar/protocol/MaestroProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MaestroProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/ManPowerProtocol.java b/src/main/java/org/traccar/protocol/ManPowerProtocol.java
index 49d8b1e9f..492e86605 100644
--- a/src/main/java/org/traccar/protocol/ManPowerProtocol.java
+++ b/src/main/java/org/traccar/protocol/ManPowerProtocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class ManPowerProtocol extends BaseProtocol {
- public ManPowerProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public ManPowerProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ';'));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/ManPowerProtocolDecoder.java b/src/main/java/org/traccar/protocol/ManPowerProtocolDecoder.java
index 2c7b7eb40..8ac13b4d4 100644
--- a/src/main/java/org/traccar/protocol/ManPowerProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ManPowerProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/Mavlink2Protocol.java b/src/main/java/org/traccar/protocol/Mavlink2Protocol.java
index d779648e4..cf65a2db3 100644
--- a/src/main/java/org/traccar/protocol/Mavlink2Protocol.java
+++ b/src/main/java/org/traccar/protocol/Mavlink2Protocol.java
@@ -15,18 +15,21 @@
*/
package org.traccar.protocol;
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
-import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import javax.inject.Inject;
public class Mavlink2Protocol extends BaseProtocol {
- public Mavlink2Protocol() {
- addServer(new TrackerServer(true, getName()) {
+ @Inject
+ public Mavlink2Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 1, 1, 10, 0));
pipeline.addLast(new Mavlink2ProtocolDecoder(Mavlink2Protocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/Mavlink2ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Mavlink2ProtocolDecoder.java
index 431258388..fac930ba8 100644
--- a/src/main/java/org/traccar/protocol/Mavlink2ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Mavlink2ProtocolDecoder.java
@@ -18,7 +18,7 @@ package org.traccar.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
diff --git a/src/main/java/org/traccar/protocol/MegastekProtocol.java b/src/main/java/org/traccar/protocol/MegastekProtocol.java
index e9f5f9fde..10215eb7c 100644
--- a/src/main/java/org/traccar/protocol/MegastekProtocol.java
+++ b/src/main/java/org/traccar/protocol/MegastekProtocol.java
@@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class MegastekProtocol extends BaseProtocol {
- public MegastekProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public MegastekProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new MegastekFrameDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/MegastekProtocolDecoder.java b/src/main/java/org/traccar/protocol/MegastekProtocolDecoder.java
index 7233280c2..06b6f0e76 100644
--- a/src/main/java/org/traccar/protocol/MegastekProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MegastekProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/MeiligaoProtocol.java b/src/main/java/org/traccar/protocol/MeiligaoProtocol.java
index e8a66e49f..492094ce3 100644
--- a/src/main/java/org/traccar/protocol/MeiligaoProtocol.java
+++ b/src/main/java/org/traccar/protocol/MeiligaoProtocol.java
@@ -18,31 +18,36 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class MeiligaoProtocol extends BaseProtocol {
- public MeiligaoProtocol() {
+ @Inject
+ public MeiligaoProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_POSITION_SINGLE,
Command.TYPE_POSITION_PERIODIC,
+ Command.TYPE_OUTPUT_CONTROL,
Command.TYPE_ENGINE_STOP,
Command.TYPE_ENGINE_RESUME,
Command.TYPE_ALARM_GEOFENCE,
Command.TYPE_SET_TIMEZONE,
Command.TYPE_REQUEST_PHOTO,
Command.TYPE_REBOOT_DEVICE);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new MeiligaoFrameDecoder());
pipeline.addLast(new MeiligaoProtocolEncoder(MeiligaoProtocol.this));
pipeline.addLast(new MeiligaoProtocolDecoder(MeiligaoProtocol.this));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new MeiligaoProtocolEncoder(MeiligaoProtocol.this));
pipeline.addLast(new MeiligaoProtocolDecoder(MeiligaoProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java
index d38e5d1c3..f3b56973a 100644
--- a/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java
@@ -19,8 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
@@ -40,7 +39,7 @@ import java.util.regex.Pattern;
public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
- private Map<Byte, ByteBuf> photos = new HashMap<>();
+ private final Map<Byte, ByteBuf> photos = new HashMap<>();
public MeiligaoProtocolDecoder(Protocol protocol) {
super(protocol);
@@ -469,10 +468,9 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
} else if (command == MSG_POSITION_IMAGE) {
byte imageIndex = buf.readByte();
buf.readUnsignedByte(); // image upload type
- String uniqueId = Context.getIdentityManager().getById(deviceSession.getDeviceId()).getUniqueId();
ByteBuf photo = photos.remove(imageIndex);
try {
- position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(uniqueId, photo, "jpg"));
+ position.set(Position.KEY_IMAGE, writeMediaFile(deviceSession.getUniqueId(), photo, "jpg"));
} finally {
photo.release();
}
diff --git a/src/main/java/org/traccar/protocol/MeiligaoProtocolEncoder.java b/src/main/java/org/traccar/protocol/MeiligaoProtocolEncoder.java
index e5b2cf4e7..5859d91ce 100644
--- a/src/main/java/org/traccar/protocol/MeiligaoProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/MeiligaoProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,13 +18,16 @@ package org.traccar.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.traccar.BaseProtocolEncoder;
-import org.traccar.Context;
+import org.traccar.Protocol;
+import org.traccar.config.Keys;
import org.traccar.helper.Checksum;
import org.traccar.helper.DataConverter;
+import org.traccar.helper.model.AttributeUtil;
import org.traccar.model.Command;
-import org.traccar.Protocol;
+import org.traccar.model.Device;
import java.nio.charset.StandardCharsets;
+import java.util.Set;
import java.util.TimeZone;
public class MeiligaoProtocolEncoder extends BaseProtocolEncoder {
@@ -56,15 +59,36 @@ public class MeiligaoProtocolEncoder extends BaseProtocolEncoder {
return buf;
}
- @Override
- protected Object encodeCommand(Command command) {
+ private ByteBuf encodeOutputCommand(long deviceId, int index, int value) {
- boolean alternative = Context.getIdentityManager().lookupAttributeBoolean(
- command.getDeviceId(), getProtocolName() + ".alternative", false, false, true);
+ int outputCount;
+ int outputType;
- int outputControlMessageType = alternative
- ? MeiligaoProtocolDecoder.MSG_OUTPUT_CONTROL_1
- : MeiligaoProtocolDecoder.MSG_OUTPUT_CONTROL_2;
+ String model = getCacheManager().getObject(Device.class, deviceId).getModel();
+
+ if (model != null && Set.of("TK510", "GT08", "TK208", "TK228", "MT05").contains(model)) {
+ outputCount = 5;
+ outputType = MeiligaoProtocolDecoder.MSG_OUTPUT_CONTROL_1;
+ } else {
+ outputCount = 1;
+ boolean alternative = AttributeUtil.lookup(
+ getCacheManager(), Keys.PROTOCOL_ALTERNATIVE.withPrefix(getProtocolName()), deviceId);
+ outputType = alternative
+ ? MeiligaoProtocolDecoder.MSG_OUTPUT_CONTROL_1
+ : MeiligaoProtocolDecoder.MSG_OUTPUT_CONTROL_2;
+ }
+
+ ByteBuf content = Unpooled.buffer();
+
+ for (int i = 1; i <= outputCount; i++) {
+ content.writeByte(i == index ? value : 2);
+ }
+
+ return encodeContent(deviceId, outputType, content);
+ }
+
+ @Override
+ protected Object encodeCommand(Command command) {
ByteBuf content = Unpooled.buffer();
@@ -74,12 +98,14 @@ public class MeiligaoProtocolEncoder extends BaseProtocolEncoder {
case Command.TYPE_POSITION_PERIODIC:
content.writeShort(command.getInteger(Command.KEY_FREQUENCY) / 10);
return encodeContent(command.getDeviceId(), MeiligaoProtocolDecoder.MSG_TRACK_BY_INTERVAL, content);
+ case Command.TYPE_OUTPUT_CONTROL:
+ int index = command.getInteger(Command.KEY_INDEX) - 1;
+ int value = command.getInteger(Command.KEY_DATA);
+ return encodeOutputCommand(command.getDeviceId(), index, value);
case Command.TYPE_ENGINE_STOP:
- content.writeByte(0x01);
- return encodeContent(command.getDeviceId(), outputControlMessageType, content);
+ return encodeOutputCommand(command.getDeviceId(), 1, 1);
case Command.TYPE_ENGINE_RESUME:
- content.writeByte(0x00);
- return encodeContent(command.getDeviceId(), outputControlMessageType, content);
+ return encodeOutputCommand(command.getDeviceId(), 1, 0);
case Command.TYPE_ALARM_GEOFENCE:
content.writeShort(command.getInteger(Command.KEY_RADIUS));
return encodeContent(command.getDeviceId(), MeiligaoProtocolDecoder.MSG_MOVEMENT_ALARM, content);
diff --git a/src/main/java/org/traccar/protocol/MeitrackProtocol.java b/src/main/java/org/traccar/protocol/MeitrackProtocol.java
index 7439ea611..c6eba8fe1 100644
--- a/src/main/java/org/traccar/protocol/MeitrackProtocol.java
+++ b/src/main/java/org/traccar/protocol/MeitrackProtocol.java
@@ -19,11 +19,15 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class MeitrackProtocol extends BaseProtocol {
- public MeitrackProtocol() {
+ @Inject
+ public MeitrackProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_POSITION_SINGLE,
Command.TYPE_ENGINE_STOP,
@@ -32,18 +36,18 @@ public class MeitrackProtocol extends BaseProtocol {
Command.TYPE_ALARM_DISARM,
Command.TYPE_REQUEST_PHOTO,
Command.TYPE_SEND_SMS);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new MeitrackFrameDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new MeitrackProtocolEncoder(MeitrackProtocol.this));
pipeline.addLast(new MeitrackProtocolDecoder(MeitrackProtocol.this));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringEncoder());
pipeline.addLast(new MeitrackProtocolEncoder(MeitrackProtocol.this));
pipeline.addLast(new MeitrackProtocolDecoder(MeitrackProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java
index 5eab10498..343141dca 100644
--- a/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,8 +19,8 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
+import org.traccar.model.Device;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Checksum;
@@ -204,18 +204,18 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.PREFIX_ADC + i, parser.nextHexInt());
}
- String deviceModel = Context.getIdentityManager().getById(deviceSession.getDeviceId()).getModel();
- if (deviceModel == null) {
- deviceModel = "";
+ String model = getCacheManager().getObject(Device.class, deviceSession.getDeviceId()).getModel();
+ if (model == null) {
+ model = "";
}
- switch (deviceModel.toUpperCase()) {
+ switch (model.toUpperCase()) {
case "MVT340":
case "MVT380":
- position.set(Position.KEY_BATTERY, parser.nextHexInt(0) * 3.0 * 2.0 / 1024.0);
+ position.set(Position.KEY_BATTERY, parser.nextHexInt() * 3.0 * 2.0 / 1024.0);
position.set(Position.KEY_POWER, parser.nextHexInt(0) * 3.0 * 16.0 / 1024.0);
break;
case "MT90":
- position.set(Position.KEY_BATTERY, parser.nextHexInt(0) * 3.3 * 2.0 / 4096.0);
+ position.set(Position.KEY_BATTERY, parser.nextHexInt() * 3.3 * 2.0 / 4096.0);
position.set(Position.KEY_POWER, parser.nextHexInt(0));
break;
case "T1":
@@ -225,19 +225,18 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
case "MVT800":
case "TC68":
case "TC68S":
- position.set(Position.KEY_BATTERY, parser.nextHexInt(0) * 3.3 * 2.0 / 4096.0);
+ position.set(Position.KEY_BATTERY, parser.nextHexInt() * 3.3 * 2.0 / 4096.0);
position.set(Position.KEY_POWER, parser.nextHexInt(0) * 3.3 * 16.0 / 4096.0);
break;
case "T311":
case "T322X":
case "T333":
case "T355":
- position.set(Position.KEY_BATTERY, parser.nextHexInt(0) / 100.0);
- position.set(Position.KEY_POWER, parser.nextHexInt(0) / 100.0);
- break;
+ case "T366":
+ case "T366G":
default:
- position.set(Position.KEY_BATTERY, parser.nextHexInt(0));
- position.set(Position.KEY_POWER, parser.nextHexInt(0));
+ position.set(Position.KEY_BATTERY, parser.nextHexInt() / 100.0);
+ position.set(Position.KEY_POWER, parser.nextHexInt(0) / 100.0);
break;
}
@@ -368,13 +367,9 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
StringBuilder command = new StringBuilder("@@");
command.append(flag).append(27 + positions.size() / 10).append(",");
command.append(imei).append(",CCC,").append(positions.size()).append("*");
- int checksum = 0;
- for (int i = 0; i < command.length(); i += 1) {
- checksum += command.charAt(i);
- }
- command.append(String.format("%02x", checksum & 0xff).toUpperCase());
+ command.append(Checksum.sum(command.toString()));
command.append("\r\n");
- channel.writeAndFlush(new NetworkMessage(command.toString(), remoteAddress)); // delete processed data
+ channel.writeAndFlush(new NetworkMessage(command.toString(), remoteAddress));
}
return positions;
@@ -404,7 +399,8 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
int paramCount = buf.readUnsignedByte();
for (int j = 0; j < paramCount; j++) {
- int id = buf.readUnsignedByte();
+ boolean extension = buf.getUnsignedByte(buf.readerIndex()) == 0xFE;
+ int id = extension ? buf.readUnsignedShort() : buf.readUnsignedByte();
switch (id) {
case 0x01:
position.set(Position.KEY_EVENT, buf.readUnsignedByte());
@@ -418,12 +414,21 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
case 0x07:
position.set(Position.KEY_RSSI, buf.readUnsignedByte());
break;
+ case 0x14:
+ position.set(Position.KEY_OUTPUT, buf.readUnsignedByte());
+ break;
+ case 0x15:
+ position.set(Position.KEY_INPUT, buf.readUnsignedByte());
+ break;
case 0x97:
position.set(Position.KEY_THROTTLE, buf.readUnsignedByte());
break;
case 0x9D:
position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedByte());
break;
+ case 0xFE69:
+ position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
+ break;
default:
buf.readUnsignedByte();
break;
@@ -432,7 +437,8 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
paramCount = buf.readUnsignedByte();
for (int j = 0; j < paramCount; j++) {
- int id = buf.readUnsignedByte();
+ boolean extension = buf.getUnsignedByte(buf.readerIndex()) == 0xFE;
+ int id = extension ? buf.readUnsignedShort() : buf.readUnsignedByte();
switch (id) {
case 0x08:
position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShortLE()));
@@ -446,6 +452,9 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
case 0x0B:
position.setAltitude(buf.readShortLE());
break;
+ case 0x16:
+ position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShortLE() * 0.01);
+ break;
case 0x19:
position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() * 0.01);
break;
@@ -482,7 +491,8 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
paramCount = buf.readUnsignedByte();
for (int j = 0; j < paramCount; j++) {
- int id = buf.readUnsignedByte();
+ boolean extension = buf.getUnsignedByte(buf.readerIndex()) == 0xFE;
+ int id = extension ? buf.readUnsignedShort() : buf.readUnsignedByte();
switch (id) {
case 0x02:
position.setLatitude(buf.readIntLE() * 0.000001);
@@ -514,8 +524,30 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
paramCount = buf.readUnsignedByte();
for (int j = 0; j < paramCount; j++) {
- buf.readUnsignedByte(); // id
- buf.skipBytes(buf.readUnsignedByte()); // value
+ boolean extension = buf.getUnsignedByte(buf.readerIndex()) == 0xFE;
+ int id = extension ? buf.readUnsignedShort() : buf.readUnsignedByte();
+ int length = buf.readUnsignedByte();
+ switch (id) {
+ case 0x2A:
+ case 0x2B:
+ case 0x2C:
+ case 0x2D:
+ case 0x2E:
+ case 0x2F:
+ case 0x30:
+ case 0x31:
+ buf.readUnsignedByte(); // label
+ position.set(Position.PREFIX_TEMP + (id - 0x2A), buf.readShortLE() * 0.01);
+ break;
+ case 0xFE31:
+ buf.readUnsignedByte(); // alarm protocol
+ buf.readUnsignedByte(); // alarm type
+ buf.skipBytes(length - 2);
+ break;
+ default:
+ buf.skipBytes(length);
+ break;
+ }
}
positions.add(position);
@@ -524,13 +556,13 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
return positions;
}
- private void requestPhotoPacket(Channel channel, SocketAddress socketAddress, String imei, String file, int index) {
+ private void requestPhotoPacket(Channel channel, SocketAddress remoteAddress, String imei, String file, int index) {
if (channel != null) {
String content = "D00," + file + "," + index;
int length = 1 + imei.length() + 1 + content.length() + 5;
String response = String.format("@@O%02d,%s,%s*", length, imei, content);
response += Checksum.sum(response) + "\r\n";
- channel.writeAndFlush(new NetworkMessage(response, socketAddress));
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
}
@@ -546,6 +578,13 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
String type = buf.toString(index + 1, 3, StandardCharsets.US_ASCII);
switch (type) {
+ case "AAC":
+ if (channel != null) {
+ String response = String.format("@@z27,%s,AAC,1*", imei);
+ response += Checksum.sum(response) + "\r\n";
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ return null;
case "D00":
if (photo == null) {
photo = Unpooled.buffer();
@@ -570,7 +609,7 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
getLastLocation(position, null);
- position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(imei, photo, "jpg"));
+ position.set(Position.KEY_IMAGE, writeMediaFile(imei, photo, "jpg"));
photo.release();
photo = null;
diff --git a/src/main/java/org/traccar/protocol/MeitrackProtocolEncoder.java b/src/main/java/org/traccar/protocol/MeitrackProtocolEncoder.java
index 354e81434..365dbb35a 100644
--- a/src/main/java/org/traccar/protocol/MeitrackProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/MeitrackProtocolEncoder.java
@@ -15,11 +15,12 @@
*/
package org.traccar.protocol;
-import org.traccar.Context;
+import org.traccar.Protocol;
import org.traccar.StringProtocolEncoder;
+import org.traccar.config.Keys;
import org.traccar.helper.Checksum;
+import org.traccar.helper.model.AttributeUtil;
import org.traccar.model.Command;
-import org.traccar.Protocol;
import java.util.Map;
@@ -42,8 +43,8 @@ public class MeitrackProtocolEncoder extends StringProtocolEncoder {
Map<String, Object> attributes = command.getAttributes();
- boolean alternative = Context.getIdentityManager().lookupAttributeBoolean(
- command.getDeviceId(), getProtocolName() + ".alternative", false, false, true);
+ boolean alternative = AttributeUtil.lookup(
+ getCacheManager(), Keys.PROTOCOL_ALTERNATIVE.withPrefix(getProtocolName()), command.getDeviceId());
switch (command.getType()) {
case Command.TYPE_POSITION_SINGLE:
diff --git a/src/main/java/org/traccar/protocol/MictrackProtocol.java b/src/main/java/org/traccar/protocol/MictrackProtocol.java
index 9fd9666e4..ccbc4db4c 100644
--- a/src/main/java/org/traccar/protocol/MictrackProtocol.java
+++ b/src/main/java/org/traccar/protocol/MictrackProtocol.java
@@ -20,21 +20,25 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class MictrackProtocol extends BaseProtocol {
- public MictrackProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public MictrackProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new MictrackProtocolDecoder(MictrackProtocol.this));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new MictrackProtocolDecoder(MictrackProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java
index 652ba3f6a..84ba75e7c 100644
--- a/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2020 Roeland Boeters (roeland@geodelta.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,7 +18,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
@@ -114,14 +114,18 @@ public class MictrackProtocolDecoder extends BaseProtocolDecoder {
}
}
- private void decodeWifi(Network network, String data) {
+ private void decodeWifi(Network network, String data, boolean hasSsid) {
String[] values = data.split(",");
- for (int i = 0; i < values.length / 2; i++) {
- network.addWifiAccessPoint(WifiAccessPoint.from(values[i * 2], Integer.parseInt(values[i * 2 + 1])));
+ int step = hasSsid ? 3 : 2;
+ int offset = hasSsid ? 1 : 0;
+ for (int i = 0; i < values.length / step; i++) {
+ network.addWifiAccessPoint(WifiAccessPoint.from(
+ values[i * step + offset], Integer.parseInt(values[i * step + offset + 1])));
}
}
- private void decodeNetwork(Position position, String data, boolean hasWifi, boolean hasCell) throws ParseException {
+ private void decodeNetwork(
+ Position position, String data, boolean hasWifi, boolean hasSsid, boolean hasCell) throws ParseException {
int index = 0;
String[] values = data.split("\\+");
@@ -130,7 +134,7 @@ public class MictrackProtocolDecoder extends BaseProtocolDecoder {
Network network = new Network();
if (hasWifi) {
- decodeWifi(network, values[index++]);
+ decodeWifi(network, values[index++], hasSsid);
}
if (hasCell) {
@@ -231,19 +235,22 @@ public class MictrackProtocolDecoder extends BaseProtocolDecoder {
decodeLocation(position, fragments[4]);
break;
case "R1":
- decodeNetwork(position, fragments[4], true, false);
+ decodeNetwork(position, fragments[4], true, false, false);
break;
case "R2":
case "R3":
- decodeNetwork(position, fragments[4], false, true);
+ decodeNetwork(position, fragments[4], false, false, true);
break;
case "R12":
case "R13":
- decodeNetwork(position, fragments[4], true, true);
+ decodeNetwork(position, fragments[4], true, false, true);
break;
case "RH":
decodeStatus(position, fragments[4]);
break;
+ case "Y1":
+ decodeNetwork(position, fragments[4], true, true, false);
+ break;
default:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/MilesmateProtocol.java b/src/main/java/org/traccar/protocol/MilesmateProtocol.java
index 822711603..59212e791 100644
--- a/src/main/java/org/traccar/protocol/MilesmateProtocol.java
+++ b/src/main/java/org/traccar/protocol/MilesmateProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class MilesmateProtocol extends BaseProtocol {
- public MilesmateProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public MilesmateProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/MilesmateProtocolDecoder.java b/src/main/java/org/traccar/protocol/MilesmateProtocolDecoder.java
index 901ceb8f7..21c629411 100644
--- a/src/main/java/org/traccar/protocol/MilesmateProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MilesmateProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
diff --git a/src/main/java/org/traccar/protocol/MiniFinderProtocol.java b/src/main/java/org/traccar/protocol/MiniFinderProtocol.java
index 82534ecd8..44599accc 100644
--- a/src/main/java/org/traccar/protocol/MiniFinderProtocol.java
+++ b/src/main/java/org/traccar/protocol/MiniFinderProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,11 +21,15 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class MiniFinderProtocol extends BaseProtocol {
- public MiniFinderProtocol() {
+ @Inject
+ public MiniFinderProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_SET_TIMEZONE,
Command.TYPE_VOICE_MONITORING,
@@ -38,10 +42,10 @@ public class MiniFinderProtocol extends BaseProtocol {
Command.TYPE_MODE_DEEP_SLEEP,
Command.TYPE_SOS_NUMBER,
Command.TYPE_SET_INDICATOR);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
- pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ';'));
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ";\0", ";"));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new MiniFinderProtocolEncoder(MiniFinderProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/MiniFinderProtocolDecoder.java b/src/main/java/org/traccar/protocol/MiniFinderProtocolDecoder.java
index 2b7a960c4..f2e5eb905 100644
--- a/src/main/java/org/traccar/protocol/MiniFinderProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MiniFinderProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.Parser;
@@ -143,7 +143,7 @@ public class MiniFinderProtocolDecoder extends BaseProtocolDecoder {
}
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
- if (deviceSession == null || !sentence.matches("![3A-D],.*")) {
+ if (deviceSession == null || !sentence.matches("![35A-D],.*")) {
return null;
}
@@ -161,6 +161,19 @@ public class MiniFinderProtocolDecoder extends BaseProtocolDecoder {
return position;
+ } else if (type.equals("5")) {
+
+ String[] values = sentence.split(",");
+
+ getLastLocation(position, null);
+
+ position.set(Position.KEY_RSSI, Integer.parseInt(values[1]));
+ if (values.length >= 4) {
+ position.set(Position.KEY_BATTERY_LEVEL, Integer.parseInt(values[3]));
+ }
+
+ return position;
+
} else if (type.equals("B") || type.equals("D")) {
Parser parser = new Parser(PATTERN_BD, sentence);
diff --git a/src/main/java/org/traccar/protocol/Minifinder2Protocol.java b/src/main/java/org/traccar/protocol/Minifinder2Protocol.java
index f8801db74..5499a274e 100644
--- a/src/main/java/org/traccar/protocol/Minifinder2Protocol.java
+++ b/src/main/java/org/traccar/protocol/Minifinder2Protocol.java
@@ -19,15 +19,19 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import java.nio.ByteOrder;
+import javax.inject.Inject;
+
public class Minifinder2Protocol extends BaseProtocol {
- public Minifinder2Protocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public Minifinder2Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1200, 2, 2, 4, 0, true));
pipeline.addLast(new Minifinder2ProtocolDecoder(Minifinder2Protocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java
index 641a45864..0b08badb8 100644
--- a/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
@@ -50,7 +50,7 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_SERVICES = 0x03;
public static final int MSG_RESPONSE = 0x7F;
- private String decodeAlarm(int code) {
+ private String decodeAlarm(long code) {
if (BitUtil.check(code, 0)) {
return Position.ALARM_LOW_BATTERY;
}
@@ -181,7 +181,11 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
position.setDeviceId(deviceSession.getDeviceId());
break;
case 0x02:
- position.set(Position.KEY_ALARM, decodeAlarm(buf.readIntLE()));
+ long alarm = buf.readUnsignedIntLE();
+ position.set(Position.KEY_ALARM, decodeAlarm(alarm));
+ if (BitUtil.check(alarm, 31)) {
+ position.set("bark", true);
+ }
break;
case 0x14:
position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
@@ -194,7 +198,9 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShortLE()));
position.setCourse(buf.readUnsignedShortLE());
position.setAltitude(buf.readShortLE());
- position.setValid(buf.readUnsignedShortLE() > 0);
+ int hdop = buf.readUnsignedShortLE();
+ position.setValid(hdop > 0);
+ position.set(Position.KEY_HDOP, hdop * 0.1);
position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
break;
@@ -260,8 +266,16 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
hasLocation = true;
break;
case 0x30:
- buf.readUnsignedInt(); // timestamp
- position.set(Position.KEY_STEPS, buf.readUnsignedInt());
+ buf.readUnsignedIntLE(); // timestamp
+ position.set(Position.KEY_STEPS, buf.readUnsignedIntLE());
+ break;
+ case 0x31:
+ int i = 1;
+ while (buf.readerIndex() < endIndex) {
+ position.set("activity" + i + "Time", buf.readUnsignedIntLE());
+ position.set("activity" + i, buf.readUnsignedIntLE());
+ i += 1;
+ }
break;
case 0x40:
buf.readUnsignedIntLE(); // timestamp
diff --git a/src/main/java/org/traccar/protocol/MobilogixProtocol.java b/src/main/java/org/traccar/protocol/MobilogixProtocol.java
index 28380a2af..1b06c2249 100644
--- a/src/main/java/org/traccar/protocol/MobilogixProtocol.java
+++ b/src/main/java/org/traccar/protocol/MobilogixProtocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class MobilogixProtocol extends BaseProtocol {
- public MobilogixProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public MobilogixProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ']'));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/MobilogixProtocolDecoder.java b/src/main/java/org/traccar/protocol/MobilogixProtocolDecoder.java
index f3b70e40c..d7600ecbb 100644
--- a/src/main/java/org/traccar/protocol/MobilogixProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MobilogixProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
@@ -47,9 +47,9 @@ public class MobilogixProtocolDecoder extends BaseProtocolDecoder {
.number("(d+.d+)") // battery
.groupBegin()
.text(",")
- .number("(d)") // valid
+ .number("(d)") // satellites
.number("(d)") // rssi
- .number("(d),") // satellites
+ .number("(d),") // valid
.number("(-?d+.d+),") // latitude
.number("(-?d+.d+),") // longitude
.number("(d+.?d*),") // speed
@@ -127,12 +127,12 @@ public class MobilogixProtocolDecoder extends BaseProtocolDecoder {
if (parser.hasNext(7)) {
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_RSSI, 6 * parser.nextInt() - 111);
+
position.setValid(parser.nextInt() > 0);
position.setFixTime(position.getDeviceTime());
- position.set(Position.KEY_RSSI, parser.nextInt());
- position.set(Position.KEY_SATELLITES, parser.nextInt());
-
position.setLatitude(parser.nextDouble());
position.setLongitude(parser.nextDouble());
position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
diff --git a/src/main/java/org/traccar/protocol/MoovboxProtocol.java b/src/main/java/org/traccar/protocol/MoovboxProtocol.java
index 7b554266f..16438e122 100644
--- a/src/main/java/org/traccar/protocol/MoovboxProtocol.java
+++ b/src/main/java/org/traccar/protocol/MoovboxProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class MoovboxProtocol extends BaseProtocol {
- public MoovboxProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public MoovboxProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new HttpResponseEncoder());
pipeline.addLast(new HttpRequestDecoder());
pipeline.addLast(new HttpObjectAggregator(65535));
diff --git a/src/main/java/org/traccar/protocol/MoovboxProtocolDecoder.java b/src/main/java/org/traccar/protocol/MoovboxProtocolDecoder.java
index 3116d073c..8e6679b05 100644
--- a/src/main/java/org/traccar/protocol/MoovboxProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MoovboxProtocolDecoder.java
@@ -20,7 +20,7 @@ import io.netty.channel.Channel;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import org.traccar.BaseHttpProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.model.Position;
import org.w3c.dom.Document;
diff --git a/src/main/java/org/traccar/protocol/MotorProtocol.java b/src/main/java/org/traccar/protocol/MotorProtocol.java
index 680687e15..3101c9b75 100644
--- a/src/main/java/org/traccar/protocol/MotorProtocol.java
+++ b/src/main/java/org/traccar/protocol/MotorProtocol.java
@@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class MotorProtocol extends BaseProtocol {
- public MotorProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public MotorProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new MotorProtocolDecoder(MotorProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/MotorProtocolDecoder.java b/src/main/java/org/traccar/protocol/MotorProtocolDecoder.java
index 8ce4fe8b1..9bca4d9bc 100644
--- a/src/main/java/org/traccar/protocol/MotorProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MotorProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BcdUtil;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/Mta6Protocol.java b/src/main/java/org/traccar/protocol/Mta6Protocol.java
index 14a66ce5c..019fe4fa9 100644
--- a/src/main/java/org/traccar/protocol/Mta6Protocol.java
+++ b/src/main/java/org/traccar/protocol/Mta6Protocol.java
@@ -19,22 +19,25 @@ import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import org.traccar.BaseProtocol;
-import org.traccar.Context;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.config.Keys;
+import javax.inject.Inject;
+
public class Mta6Protocol extends BaseProtocol {
- public Mta6Protocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public Mta6Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new HttpResponseEncoder());
pipeline.addLast(new HttpRequestDecoder());
pipeline.addLast(new HttpObjectAggregator(65535));
pipeline.addLast(new Mta6ProtocolDecoder(
- Mta6Protocol.this, !Context.getConfig().getBoolean(Keys.PROTOCOL_CAN.withPrefix(getName()))));
+ Mta6Protocol.this, !config.getBoolean(Keys.PROTOCOL_CAN.withPrefix(getName()))));
}
});
}
diff --git a/src/main/java/org/traccar/protocol/Mta6ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Mta6ProtocolDecoder.java
index 88419b871..896c7a2d2 100644
--- a/src/main/java/org/traccar/protocol/Mta6ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Mta6ProtocolDecoder.java
@@ -26,7 +26,7 @@ import io.netty.handler.codec.http.HttpVersion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/MtxProtocol.java b/src/main/java/org/traccar/protocol/MtxProtocol.java
index 44372ce83..e085b6221 100644
--- a/src/main/java/org/traccar/protocol/MtxProtocol.java
+++ b/src/main/java/org/traccar/protocol/MtxProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class MtxProtocol extends BaseProtocol {
- public MtxProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public MtxProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/MtxProtocolDecoder.java b/src/main/java/org/traccar/protocol/MtxProtocolDecoder.java
index d1207bedf..e94d12b36 100644
--- a/src/main/java/org/traccar/protocol/MtxProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MtxProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/MxtProtocol.java b/src/main/java/org/traccar/protocol/MxtProtocol.java
index dbe43fe45..1190bf527 100644
--- a/src/main/java/org/traccar/protocol/MxtProtocol.java
+++ b/src/main/java/org/traccar/protocol/MxtProtocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class MxtProtocol extends BaseProtocol {
- public MxtProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public MxtProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new MxtFrameDecoder());
pipeline.addLast(new MxtProtocolDecoder(MxtProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/MxtProtocolDecoder.java b/src/main/java/org/traccar/protocol/MxtProtocolDecoder.java
index 379b610e1..b3e2295e8 100644
--- a/src/main/java/org/traccar/protocol/MxtProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MxtProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/NavigilProtocol.java b/src/main/java/org/traccar/protocol/NavigilProtocol.java
index 2c946c39f..46a6c33a5 100644
--- a/src/main/java/org/traccar/protocol/NavigilProtocol.java
+++ b/src/main/java/org/traccar/protocol/NavigilProtocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class NavigilProtocol extends BaseProtocol {
- public NavigilProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public NavigilProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new NavigilFrameDecoder());
pipeline.addLast(new NavigilProtocolDecoder(NavigilProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/NavigilProtocolDecoder.java b/src/main/java/org/traccar/protocol/NavigilProtocolDecoder.java
index db5521201..6dadbc559 100644
--- a/src/main/java/org/traccar/protocol/NavigilProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/NavigilProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Checksum;
diff --git a/src/main/java/org/traccar/protocol/NavisProtocol.java b/src/main/java/org/traccar/protocol/NavisProtocol.java
index d5af6838d..640a77803 100644
--- a/src/main/java/org/traccar/protocol/NavisProtocol.java
+++ b/src/main/java/org/traccar/protocol/NavisProtocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class NavisProtocol extends BaseProtocol {
- public NavisProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public NavisProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new NavisFrameDecoder());
pipeline.addLast(new NavisProtocolDecoder(NavisProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/NavisProtocolDecoder.java b/src/main/java/org/traccar/protocol/NavisProtocolDecoder.java
index 7ba474ae0..77158b315 100644
--- a/src/main/java/org/traccar/protocol/NavisProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/NavisProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,12 +19,11 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.Checksum;
-import org.traccar.helper.Checksum.Algorithm;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
@@ -591,10 +590,8 @@ public class NavisProtocolDecoder extends BaseProtocolDecoder {
private void sendFlexReply(Channel channel, ByteBuf data) {
if (channel != null) {
- ByteBuf cs = Unpooled.buffer(1);
- cs.writeByte(Checksum.crc8(new Algorithm(8, 0x31, 0xFF, false, false, 0x00), data.nioBuffer()));
-
- channel.writeAndFlush(new NetworkMessage(Unpooled.wrappedBuffer(data, cs), channel.remoteAddress()));
+ data.writeByte(Checksum.crc8(Checksum.CRC8_EGTS, data.nioBuffer()));
+ channel.writeAndFlush(new NetworkMessage(data, channel.remoteAddress()));
}
}
diff --git a/src/main/java/org/traccar/protocol/NavisetProtocol.java b/src/main/java/org/traccar/protocol/NavisetProtocol.java
index 78755ea4d..388f141f8 100644
--- a/src/main/java/org/traccar/protocol/NavisetProtocol.java
+++ b/src/main/java/org/traccar/protocol/NavisetProtocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class NavisetProtocol extends BaseProtocol {
- public NavisetProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public NavisetProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new NavisetFrameDecoder());
pipeline.addLast(new NavisetProtocolDecoder(NavisetProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/NavisetProtocolDecoder.java b/src/main/java/org/traccar/protocol/NavisetProtocolDecoder.java
index 10d71d76c..47d10b310 100644
--- a/src/main/java/org/traccar/protocol/NavisetProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/NavisetProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/NavtelecomFrameDecoder.java b/src/main/java/org/traccar/protocol/NavtelecomFrameDecoder.java
index 0fb82528b..2ab7d11a9 100644
--- a/src/main/java/org/traccar/protocol/NavtelecomFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/NavtelecomFrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,7 +30,12 @@ public class NavtelecomFrameDecoder extends BaseFrameDecoder {
protected Object decode(
ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
- if (buf.getByte(buf.readerIndex()) == '@') {
+ if (buf.getByte(buf.readerIndex()) == 0x7f) {
+
+ buf.skipBytes(1);
+ return null;
+
+ } else if (buf.getByte(buf.readerIndex()) == '@') {
int length = buf.getUnsignedShortLE(12) + 12 + 2 + 2;
if (buf.readableBytes() >= length) {
diff --git a/src/main/java/org/traccar/protocol/NavtelecomProtocol.java b/src/main/java/org/traccar/protocol/NavtelecomProtocol.java
index 29ce8c41e..50013d1a4 100644
--- a/src/main/java/org/traccar/protocol/NavtelecomProtocol.java
+++ b/src/main/java/org/traccar/protocol/NavtelecomProtocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class NavtelecomProtocol extends BaseProtocol {
- public NavtelecomProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public NavtelecomProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new NavtelecomFrameDecoder());
pipeline.addLast(new NavtelecomProtocolDecoder(NavtelecomProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java b/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java
index bdcc12c4c..08b1a8d0f 100644
--- a/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
@@ -195,6 +195,9 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder {
for (int j = 0; j < bits.length(); j++) {
if (bits.get(j)) {
+
+ int value;
+
switch (j + 1) {
case 1:
position.set(Position.KEY_INDEX, buf.readUnsignedIntLE());
@@ -205,8 +208,12 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder {
case 3:
position.setDeviceTime(new Date(buf.readUnsignedIntLE() * 1000));
break;
+ case 8:
+ value = buf.readUnsignedByte();
+ position.setValid(BitUtil.check(value, 1));
+ position.set(Position.KEY_SATELLITES, BitUtil.from(value, 2));
+ break;
case 9:
- position.setValid(true);
position.setFixTime(new Date(buf.readUnsignedIntLE() * 1000));
break;
case 10:
@@ -221,6 +228,83 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder {
case 13:
position.setSpeed(UnitsConverter.knotsFromKph(buf.readFloatLE()));
break;
+ case 14:
+ position.setCourse(buf.readUnsignedShortLE());
+ break;
+ case 15:
+ position.set(Position.KEY_ODOMETER, buf.readFloatLE());
+ break;
+ case 19:
+ position.set(Position.KEY_POWER, buf.readShortLE() * 0.001);
+ break;
+ case 20:
+ position.set(Position.KEY_BATTERY, buf.readShortLE() * 0.001);
+ break;
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ case 26:
+ position.set(Position.PREFIX_ADC + (j + 2 - 21), buf.readUnsignedShortLE() * 0.001);
+ break;
+ case 29:
+ value = buf.readUnsignedByte();
+ for (int k = 0; k <= 7; k++) {
+ position.set(Position.PREFIX_IN + (k + 1), BitUtil.check(value, k));
+ }
+ break;
+ case 31:
+ value = buf.readUnsignedByte();
+ for (int k = 0; k <= 3; k++) {
+ position.set(Position.PREFIX_OUT + (k + 1), BitUtil.check(value, k));
+ }
+ break;
+ case 33:
+ case 34:
+ position.set(Position.PREFIX_COUNT + (j + 2 - 33), buf.readUnsignedIntLE());
+ break;
+ case 35:
+ case 36:
+ position.set("freq" + (j + 2 - 35), buf.readUnsignedShortLE());
+ break;
+ case 37:
+ position.set(Position.KEY_HOURS, buf.readUnsignedIntLE());
+ break;
+ case 38:
+ case 39:
+ case 40:
+ case 41:
+ case 42:
+ case 43:
+ value = buf.readUnsignedShortLE();
+ position.set("fuel" + (j + 2 - 38), (value < 65500) ? value : null);
+ break;
+ case 44:
+ value = buf.readUnsignedShortLE();
+ position.set(Position.KEY_FUEL_LEVEL, (value < 65500) ? value : null);
+ break;
+ case 45:
+ case 46:
+ case 47:
+ case 48:
+ case 49:
+ case 50:
+ case 51:
+ case 52:
+ value = buf.readByte();
+ position.set(
+ Position.PREFIX_TEMP + (j + 2 - 45),
+ (value != (byte) 0x80) ? value : null);
+ break;
+ case 78:
+ case 79:
+ case 80:
+ case 81:
+ case 82:
+ case 83:
+ position.set("fuelTemp" + (j + 2 - 78), (int) buf.readByte());
+ break;
default:
buf.skipBytes(getItemLength(j + 1));
break;
@@ -235,12 +319,11 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder {
positions.add(position);
}
- int checksum = buf.readUnsignedByte();
if (channel != null) {
ByteBuf response = Unpooled.buffer();
response.writeCharSequence(type, StandardCharsets.US_ASCII);
response.writeByte(count);
- response.writeByte(checksum);
+ response.writeByte(Checksum.crc8(Checksum.CRC8_EGTS, response.nioBuffer()));
channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
diff --git a/src/main/java/org/traccar/protocol/NdtpV6FrameDecoder.java b/src/main/java/org/traccar/protocol/NdtpV6FrameDecoder.java
new file mode 100644
index 000000000..5b610013a
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NdtpV6FrameDecoder.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
+
+public class NdtpV6FrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ return buf;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/NdtpV6Protocol.java b/src/main/java/org/traccar/protocol/NdtpV6Protocol.java
new file mode 100644
index 000000000..ce0dbbef2
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NdtpV6Protocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
+
+public class NdtpV6Protocol extends BaseProtocol {
+
+ @Inject
+ public NdtpV6Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new NdtpV6ProtocolDecoder(NdtpV6Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/NdtpV6ProtocolDecoder.java b/src/main/java/org/traccar/protocol/NdtpV6ProtocolDecoder.java
new file mode 100644
index 000000000..35cb0bae8
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NdtpV6ProtocolDecoder.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.Checksum;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+import org.traccar.session.DeviceSession;
+
+import java.net.SocketAddress;
+import java.util.Date;
+
+public class NdtpV6ProtocolDecoder extends BaseProtocolDecoder {
+
+ public NdtpV6ProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final byte[] SIGNATURE = {0x7E, 0x7E};
+
+ private static final int NPL_FLAG_CRC = 2;
+ private static final int NPH_RESULT_OK = 0x00000000;
+ private static final int NPL_TYPE_NPH = 2;
+ private static final int NPL_ADDRESS_SERVER = 0;
+
+ private static final int NPH_RESULT = 0;
+
+ private static final int NPH_SRV_GENERIC_CONTROLS = 0;
+ private static final int NPH_SRV_NAVDATA = 1;
+
+ private static final int NPH_SGC_RESULT = NPH_RESULT;
+ private static final int NPH_SGC_CONN_REQUEST = 100;
+
+ private static final int NPH_SND_RESULT = NPH_RESULT;
+
+ private void sendResponse(
+ Channel channel, int serviceId, long requestId) {
+
+ ByteBuf content = Unpooled.buffer();
+ content.writeShortLE(serviceId);
+ content.writeIntLE(NPH_SND_RESULT);
+ content.writeIntLE((int) requestId);
+ content.writeIntLE(NPH_RESULT_OK);
+
+ ByteBuf response = Unpooled.buffer();
+ response.writeBytes(SIGNATURE);
+ response.writeShortLE(content.readableBytes());
+ response.writeShortLE(NPL_FLAG_CRC); // flags
+ response.writeShort(Checksum.crc16(Checksum.CRC16_MODBUS, content.nioBuffer()));
+ response.writeByte(NPL_TYPE_NPH); // type
+ response.writeIntLE(NPL_ADDRESS_SERVER); // peer address
+ response.writeShortLE(0); // request id
+ response.writeBytes(content);
+ content.release();
+
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+
+ private static final short MAIN_NAV_DATA = 0;
+ private static final short ADDITIONAL_NAV_DATA = 2;
+
+ private void decodeData(ByteBuf buf, Position position) {
+
+ short itemType;
+ short itemIndex;
+
+ itemType = buf.readUnsignedByte();
+ itemIndex = buf.readUnsignedByte();
+ if (itemType == MAIN_NAV_DATA && (itemIndex == 0 || itemIndex == 1)) {
+
+ position.setTime(new Date(buf.readUnsignedIntLE() * 1000));
+ position.setLongitude(buf.readIntLE() / 10000000.0);
+ position.setLatitude(buf.readIntLE() / 10000000.0);
+
+ short flags = buf.readUnsignedByte();
+ position.setValid(BitUtil.check(flags, 7));
+ if (BitUtil.check(flags, 1)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
+ }
+
+ position.set(Position.KEY_BATTERY, buf.readUnsignedByte() * 20);
+ position.set(Position.KEY_OBD_SPEED, buf.readUnsignedShortLE());
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShortLE()));
+ position.setCourse(buf.readUnsignedShortLE());
+
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedShortLE());
+ position.setAltitude(buf.readShortLE());
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ position.set(Position.KEY_PDOP, buf.readUnsignedByte());
+ }
+
+ itemType = buf.readUnsignedByte();
+ itemIndex = buf.readUnsignedByte();
+ if (itemType == ADDITIONAL_NAV_DATA && itemIndex == 0) {
+
+ position.set(Position.KEY_BATTERY_LEVEL, Math.max((buf.readUnsignedShortLE() - 3600) / 6, 100));
+ position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShortLE());
+ position.set(Position.PREFIX_ADC + 3, buf.readUnsignedShortLE());
+ position.set(Position.PREFIX_ADC + 4, buf.readUnsignedShortLE());
+
+ buf.readUnsignedByte(); // inputs
+ buf.readUnsignedByte(); // outputs
+ buf.readUnsignedShortLE(); // in1 count
+ buf.readUnsignedShortLE(); // in2 count
+ buf.readUnsignedShortLE(); // in3 count
+ buf.readUnsignedShortLE(); // in4 count
+ buf.readUnsignedIntLE(); // track length
+
+ position.set(Position.KEY_ANTENNA, buf.readUnsignedByte());
+ position.set(Position.KEY_GPS, buf.readUnsignedByte());
+ position.set(Position.KEY_ACCELERATION, buf.readUnsignedByte());
+ position.set(Position.KEY_POWER, buf.readUnsignedByte() * 200);
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+
+ buf.skipBytes(2); // signature
+ buf.readUnsignedShortLE(); // length
+ buf.readUnsignedShortLE(); // connection flags
+ buf.readUnsignedShortLE(); // checksum
+ buf.readUnsignedByte(); // type
+ buf.readUnsignedIntLE(); // address
+ buf.readUnsignedShortLE(); // identification
+
+ int serviceId = buf.readUnsignedShortLE();
+ int serviceType = buf.readUnsignedShortLE();
+ buf.readUnsignedShortLE(); // request flags
+ long requestId = buf.readUnsignedIntLE();
+
+ if (deviceSession == null && serviceId == NPH_SRV_GENERIC_CONTROLS && serviceType == NPH_SGC_CONN_REQUEST) {
+
+ buf.readUnsignedShortLE(); // version major
+ buf.readUnsignedShortLE(); // version minor
+ buf.readUnsignedShortLE(); // connection flags
+
+ int deviceId = buf.readUnsignedShortLE();
+ Position position = new Position(getProtocolName());
+ deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(deviceId));
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ if (channel != null) {
+ sendResponse(channel, serviceId, requestId);
+ }
+
+ position.set(Position.KEY_RESULT, String.valueOf(NPH_SGC_RESULT));
+ position.setTime(new Date());
+ getLastLocation(position, new Date());
+ position.setValid(false);
+
+ return position;
+
+ }
+
+ if (serviceId == NPH_SRV_NAVDATA) {
+
+ deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ if (channel != null) {
+ sendResponse(channel, serviceId, requestId);
+ }
+
+ decodeData(buf, position);
+
+ return position;
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/NdtpV6ProtocolEncoder.java b/src/main/java/org/traccar/protocol/NdtpV6ProtocolEncoder.java
new file mode 100644
index 000000000..7aac8658a
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NdtpV6ProtocolEncoder.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import org.traccar.BaseProtocolEncoder;
+import org.traccar.Protocol;
+import org.traccar.model.Command;
+
+public class NdtpV6ProtocolEncoder extends BaseProtocolEncoder {
+
+ public NdtpV6ProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object encodeCommand(Command command) {
+ switch (command.getType()) {
+ case Command.TYPE_IDENTIFICATION:
+ return "BB+IDNT";
+ case Command.TYPE_REBOOT_DEVICE:
+ return "BB+RESET";
+ case Command.TYPE_POSITION_SINGLE:
+ return "BB+RRCD";
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/NeosProtocol.java b/src/main/java/org/traccar/protocol/NeosProtocol.java
index e545a9969..0787b6562 100644
--- a/src/main/java/org/traccar/protocol/NeosProtocol.java
+++ b/src/main/java/org/traccar/protocol/NeosProtocol.java
@@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class NeosProtocol extends BaseProtocol {
- public NeosProtocol() {
- addServer(new TrackerServer(true, getName()) {
+ @Inject
+ public NeosProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new NeosProtocolDecoder(NeosProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/NeosProtocolDecoder.java b/src/main/java/org/traccar/protocol/NeosProtocolDecoder.java
index 6b5596dba..18ebc49da 100644
--- a/src/main/java/org/traccar/protocol/NeosProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/NeosProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/NetProtocol.java b/src/main/java/org/traccar/protocol/NetProtocol.java
index c114d19fc..f27e4afb8 100644
--- a/src/main/java/org/traccar/protocol/NetProtocol.java
+++ b/src/main/java/org/traccar/protocol/NetProtocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class NetProtocol extends BaseProtocol {
- public NetProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public NetProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '!'));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/NetProtocolDecoder.java b/src/main/java/org/traccar/protocol/NetProtocolDecoder.java
index c71a792a2..ebffb06f1 100644
--- a/src/main/java/org/traccar/protocol/NetProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/NetProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/NiotProtocol.java b/src/main/java/org/traccar/protocol/NiotProtocol.java
index b57b18a3a..0fbe0c689 100644
--- a/src/main/java/org/traccar/protocol/NiotProtocol.java
+++ b/src/main/java/org/traccar/protocol/NiotProtocol.java
@@ -19,13 +19,17 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class NiotProtocol extends BaseProtocol {
- public NiotProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public NiotProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 3, 2));
pipeline.addLast(new NiotProtocolDecoder(NiotProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/NiotProtocolDecoder.java b/src/main/java/org/traccar/protocol/NiotProtocolDecoder.java
index 47c6e2ffd..35614ccca 100644
--- a/src/main/java/org/traccar/protocol/NiotProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/NiotProtocolDecoder.java
@@ -20,7 +20,8 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.helper.BufferUtil;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BcdUtil;
@@ -57,12 +58,6 @@ public class NiotProtocolDecoder extends BaseProtocolDecoder {
}
}
- private double readCoordinate(ByteBuf buf) {
- long value = buf.readUnsignedInt();
- double result = BitUtil.to(value, 31) / 1800000.0;
- return BitUtil.check(value, 31) ? -result : result;
- }
-
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -96,8 +91,8 @@ public class NiotProtocolDecoder extends BaseProtocolDecoder {
.setSecond(BcdUtil.readInteger(buf, 2));
position.setTime(dateBuilder.getDate());
- position.setLatitude(readCoordinate(buf));
- position.setLongitude(readCoordinate(buf));
+ position.setLatitude(BufferUtil.readSignedMagnitudeInt(buf) / 1800000.0);
+ position.setLongitude(BufferUtil.readSignedMagnitudeInt(buf) / 1800000.0);
BcdUtil.readInteger(buf, 4); // reserved
position.setCourse(BcdUtil.readInteger(buf, 4));
diff --git a/src/main/java/org/traccar/protocol/NoranProtocol.java b/src/main/java/org/traccar/protocol/NoranProtocol.java
index 3df364c30..626991029 100644
--- a/src/main/java/org/traccar/protocol/NoranProtocol.java
+++ b/src/main/java/org/traccar/protocol/NoranProtocol.java
@@ -18,20 +18,24 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class NoranProtocol extends BaseProtocol {
- public NoranProtocol() {
+ @Inject
+ public NoranProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_POSITION_SINGLE,
Command.TYPE_POSITION_PERIODIC,
Command.TYPE_POSITION_STOP,
Command.TYPE_ENGINE_STOP,
Command.TYPE_ENGINE_RESUME);
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new NoranProtocolEncoder(NoranProtocol.this));
pipeline.addLast(new NoranProtocolDecoder(NoranProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/NoranProtocolDecoder.java b/src/main/java/org/traccar/protocol/NoranProtocolDecoder.java
index 53dae7fd6..53b58f9b6 100644
--- a/src/main/java/org/traccar/protocol/NoranProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/NoranProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/NvsProtocol.java b/src/main/java/org/traccar/protocol/NvsProtocol.java
index d319b22f3..7ed488e38 100644
--- a/src/main/java/org/traccar/protocol/NvsProtocol.java
+++ b/src/main/java/org/traccar/protocol/NvsProtocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class NvsProtocol extends BaseProtocol {
- public NvsProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public NvsProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new NvsFrameDecoder());
pipeline.addLast(new NvsProtocolDecoder(NvsProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/NvsProtocolDecoder.java b/src/main/java/org/traccar/protocol/NvsProtocolDecoder.java
index 5d1159f7d..f826c4121 100644
--- a/src/main/java/org/traccar/protocol/NvsProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/NvsProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.UnitsConverter;
diff --git a/src/main/java/org/traccar/protocol/NyitechProtocol.java b/src/main/java/org/traccar/protocol/NyitechProtocol.java
index 58974be5c..e7ef10945 100644
--- a/src/main/java/org/traccar/protocol/NyitechProtocol.java
+++ b/src/main/java/org/traccar/protocol/NyitechProtocol.java
@@ -19,15 +19,19 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import java.nio.ByteOrder;
+import javax.inject.Inject;
+
public class NyitechProtocol extends BaseProtocol {
- public NyitechProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public NyitechProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 2, 2, -4, 0, true));
pipeline.addLast(new NyitechProtocolDecoder(NyitechProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/NyitechProtocolDecoder.java b/src/main/java/org/traccar/protocol/NyitechProtocolDecoder.java
index 62b41a2ea..49bc5b824 100644
--- a/src/main/java/org/traccar/protocol/NyitechProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/NyitechProtocolDecoder.java
@@ -18,7 +18,7 @@ package org.traccar.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
diff --git a/src/main/java/org/traccar/protocol/ObdDongleProtocol.java b/src/main/java/org/traccar/protocol/ObdDongleProtocol.java
index 10a55759b..94f450426 100644
--- a/src/main/java/org/traccar/protocol/ObdDongleProtocol.java
+++ b/src/main/java/org/traccar/protocol/ObdDongleProtocol.java
@@ -19,13 +19,17 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class ObdDongleProtocol extends BaseProtocol {
- public ObdDongleProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public ObdDongleProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(1099, 20, 2, 3, 0));
pipeline.addLast(new ObdDongleProtocolDecoder(ObdDongleProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/ObdDongleProtocolDecoder.java b/src/main/java/org/traccar/protocol/ObdDongleProtocolDecoder.java
index 1c9771ce9..bf0ba6f82 100644
--- a/src/main/java/org/traccar/protocol/ObdDongleProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ObdDongleProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/OigoProtocol.java b/src/main/java/org/traccar/protocol/OigoProtocol.java
index 5056f68aa..0539bada6 100644
--- a/src/main/java/org/traccar/protocol/OigoProtocol.java
+++ b/src/main/java/org/traccar/protocol/OigoProtocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class OigoProtocol extends BaseProtocol {
- public OigoProtocol() {
- addServer(new TrackerServer(true, getName()) {
+ @Inject
+ public OigoProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new OigoProtocolDecoder(OigoProtocol.this));
}
});
diff --git a/src/main/java/org/traccar/protocol/OigoProtocolDecoder.java b/src/main/java/org/traccar/protocol/OigoProtocolDecoder.java
index b9cc71e8c..b0c7c3bc6 100644
--- a/src/main/java/org/traccar/protocol/OigoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/OigoProtocolDecoder.java
@@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/OkoProtocol.java b/src/main/java/org/traccar/protocol/OkoProtocol.java
index 9571ccc48..29c8bc1b9 100644
--- a/src/main/java/org/traccar/protocol/OkoProtocol.java
+++ b/src/main/java/org/traccar/protocol/OkoProtocol.java
@@ -20,13 +20,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class OkoProtocol extends BaseProtocol {
- public OkoProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public OkoProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '}'));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new OkoProtocolDecoder(OkoProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/OkoProtocolDecoder.java b/src/main/java/org/traccar/protocol/OkoProtocolDecoder.java
index fa35ab455..3bb62acb9 100644
--- a/src/main/java/org/traccar/protocol/OkoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/OkoProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/OmnicommProtocol.java b/src/main/java/org/traccar/protocol/OmnicommProtocol.java
index 96660cb59..dd400c779 100644
--- a/src/main/java/org/traccar/protocol/OmnicommProtocol.java
+++ b/src/main/java/org/traccar/protocol/OmnicommProtocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class OmnicommProtocol extends BaseProtocol {
- public OmnicommProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public OmnicommProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new OmnicommFrameDecoder());
pipeline.addLast(new OmnicommProtocolDecoder(OmnicommProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/OmnicommProtocolDecoder.java b/src/main/java/org/traccar/protocol/OmnicommProtocolDecoder.java
index f90d1f2b3..9d747032b 100644
--- a/src/main/java/org/traccar/protocol/OmnicommProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/OmnicommProtocolDecoder.java
@@ -21,7 +21,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/OpenGtsProtocol.java b/src/main/java/org/traccar/protocol/OpenGtsProtocol.java
index 5ef3260c6..5443b4ffc 100644
--- a/src/main/java/org/traccar/protocol/OpenGtsProtocol.java
+++ b/src/main/java/org/traccar/protocol/OpenGtsProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class OpenGtsProtocol extends BaseProtocol {
- public OpenGtsProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public OpenGtsProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new HttpResponseEncoder());
pipeline.addLast(new HttpRequestDecoder());
pipeline.addLast(new HttpObjectAggregator(16384));
diff --git a/src/main/java/org/traccar/protocol/OpenGtsProtocolDecoder.java b/src/main/java/org/traccar/protocol/OpenGtsProtocolDecoder.java
index b76cbfa85..255a81ae6 100644
--- a/src/main/java/org/traccar/protocol/OpenGtsProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/OpenGtsProtocolDecoder.java
@@ -20,7 +20,7 @@ import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.QueryStringDecoder;
import org.traccar.BaseHttpProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/OrbcommProtocol.java b/src/main/java/org/traccar/protocol/OrbcommProtocol.java
new file mode 100644
index 000000000..fb09f0abb
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/OrbcommProtocol.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.http.HttpObjectAggregator;
+import io.netty.handler.codec.http.HttpRequestEncoder;
+import io.netty.handler.codec.http.HttpResponseDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerClient;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
+
+public class OrbcommProtocol extends BaseProtocol {
+
+ @Inject
+ public OrbcommProtocol(Config config) {
+ addClient(new TrackerClient(config, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
+ pipeline.addLast(new HttpRequestEncoder());
+ pipeline.addLast(new HttpResponseDecoder());
+ pipeline.addLast(new HttpObjectAggregator(65535));
+ pipeline.addLast(new OrbcommProtocolDecoder(OrbcommProtocol.this));
+ pipeline.addLast(new OrbcommProtocolPoller(OrbcommProtocol.this, config));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/OrbcommProtocolDecoder.java b/src/main/java/org/traccar/protocol/OrbcommProtocolDecoder.java
new file mode 100644
index 000000000..1164d72a1
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/OrbcommProtocolDecoder.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.channel.Channel;
+import io.netty.handler.codec.http.FullHttpResponse;
+import org.traccar.BasePipelineFactory;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.session.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.json.JsonValue;
+import java.io.StringReader;
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.TimeZone;
+
+public class OrbcommProtocolDecoder extends BaseProtocolDecoder {
+
+ public OrbcommProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ FullHttpResponse response = (FullHttpResponse) msg;
+ String content = response.content().toString(StandardCharsets.UTF_8);
+ JsonObject json = Json.createReader(new StringReader(content)).readObject();
+
+ if (channel != null && !json.getString("NextStartUTC").isEmpty()) {
+ OrbcommProtocolPoller poller =
+ BasePipelineFactory.getHandler(channel.pipeline(), OrbcommProtocolPoller.class);
+ if (poller != null) {
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ poller.setStartTime(dateFormat.parse(json.getString("NextStartUTC")));
+ }
+ }
+
+ if (json.get("Messages").getValueType() == JsonValue.ValueType.NULL) {
+ return null;
+ }
+
+ LinkedList<Position> positions = new LinkedList<>();
+
+ JsonArray messages = json.getJsonArray("Messages");
+ for (int i = 0; i < messages.size(); i++) {
+ JsonObject message = messages.getJsonObject(i);
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, message.getString("MobileID"));
+ if (deviceSession != null) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ position.setDeviceTime(dateFormat.parse(message.getString("MessageUTC")));
+
+ JsonArray fields = message.getJsonObject("Payload").getJsonArray("Fields");
+ for (int j = 0; j < fields.size(); j++) {
+ JsonObject field = fields.getJsonObject(j);
+ String value = field.getString("Value");
+ switch (field.getString("Name").toLowerCase()) {
+ case "eventtime":
+ position.setDeviceTime(new Date(Long.parseLong(value) * 1000));
+ break;
+ case "latitude":
+ position.setLatitude(Integer.parseInt(value) / 60000.0);
+ break;
+ case "longitude":
+ position.setLongitude(Integer.parseInt(value) / 60000.0);
+ break;
+ case "speed":
+ position.setSpeed(UnitsConverter.knotsFromKph(Integer.parseInt(value)));
+ break;
+ case "heading":
+ int heading = Integer.parseInt(value);
+ position.setCourse(heading <= 360 ? heading : 0);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (position.getLatitude() != 0 && position.getLongitude() != 0) {
+ position.setValid(true);
+ position.setFixTime(position.getDeviceTime());
+ } else {
+ getLastLocation(position, position.getDeviceTime());
+ }
+
+ positions.add(position);
+
+ }
+ }
+
+ return positions.isEmpty() ? null : positions;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/OrbcommProtocolPoller.java b/src/main/java/org/traccar/protocol/OrbcommProtocolPoller.java
new file mode 100644
index 000000000..0f57bfb49
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/OrbcommProtocolPoller.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.handler.codec.http.DefaultFullHttpRequest;
+import io.netty.handler.codec.http.HttpHeaderNames;
+import io.netty.handler.codec.http.HttpMethod;
+import io.netty.handler.codec.http.HttpRequest;
+import io.netty.handler.codec.http.HttpVersion;
+import io.netty.handler.codec.http.QueryStringEncoder;
+import org.traccar.BaseProtocolPoller;
+import org.traccar.Protocol;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+
+import java.net.SocketAddress;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+public class OrbcommProtocolPoller extends BaseProtocolPoller {
+
+ private final String accessId;
+ private final String password;
+ private final String host;
+
+ private Date startTime = new Date();
+
+ public void setStartTime(Date startTime) {
+ this.startTime = startTime;
+ }
+
+ public OrbcommProtocolPoller(Protocol protocol, Config config) {
+ super(config.getLong(Keys.PROTOCOL_INTERVAL.withPrefix(protocol.getName())));
+ accessId = config.getString(Keys.ORBCOMM_ACCESS_ID);
+ password = config.getString(Keys.ORBCOMM_PASSWORD);
+ host = config.getString(Keys.PROTOCOL_ADDRESS.withPrefix(protocol.getName()));
+ }
+
+ @Override
+ protected void sendRequest(Channel channel, SocketAddress remoteAddress) {
+
+ QueryStringEncoder encoder = new QueryStringEncoder("/GLGW/2/RestMessages.svc/JSON/get_return_messages/");
+ encoder.addParam("access_id", accessId);
+ encoder.addParam("password", password);
+
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ encoder.addParam("start_utc", dateFormat.format(startTime));
+
+ HttpRequest request = new DefaultFullHttpRequest(
+ HttpVersion.HTTP_1_1, HttpMethod.GET, encoder.toString(), Unpooled.buffer());
+ request.headers().add(HttpHeaderNames.HOST, host);
+ request.headers().add(HttpHeaderNames.CONTENT_LENGTH, 0);
+ channel.writeAndFlush(request);
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/OrionProtocol.java b/src/main/java/org/traccar/protocol/OrionProtocol.java
index 8485ae638..2dec7cd06 100644
--- a/src/main/java/org/traccar/protocol/OrionProtocol.java
+++ b/src/main/java/org/traccar/protocol/OrionProtocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class OrionProtocol extends BaseProtocol {
- public OrionProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public OrionProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new OrionFrameDecoder());
pipeline.addLast(new OrionProtocolDecoder(OrionProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/OrionProtocolDecoder.java b/src/main/java/org/traccar/protocol/OrionProtocolDecoder.java
index af819989e..681891edb 100644
--- a/src/main/java/org/traccar/protocol/OrionProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/OrionProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
diff --git a/src/main/java/org/traccar/protocol/OsmAndProtocol.java b/src/main/java/org/traccar/protocol/OsmAndProtocol.java
index d3aa2fd6f..a86bc70d7 100644
--- a/src/main/java/org/traccar/protocol/OsmAndProtocol.java
+++ b/src/main/java/org/traccar/protocol/OsmAndProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class OsmAndProtocol extends BaseProtocol {
- public OsmAndProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public OsmAndProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new HttpResponseEncoder());
pipeline.addLast(new HttpRequestDecoder());
pipeline.addLast(new HttpObjectAggregator(16384));
diff --git a/src/main/java/org/traccar/protocol/OsmAndProtocolDecoder.java b/src/main/java/org/traccar/protocol/OsmAndProtocolDecoder.java
index ec9bbc240..c8968023a 100644
--- a/src/main/java/org/traccar/protocol/OsmAndProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/OsmAndProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,10 +21,8 @@ import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.QueryStringDecoder;
import org.traccar.BaseHttpProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
-import org.traccar.database.CommandsManager;
import org.traccar.helper.DateUtil;
import org.traccar.model.CellTower;
import org.traccar.model.Command;
@@ -61,6 +59,8 @@ public class OsmAndProtocolDecoder extends BaseHttpProtocolDecoder {
position.setValid(true);
Network network = new Network();
+ Double latitude = null;
+ Double longitude = null;
for (Map.Entry<String, List<String>> entry : params.entrySet()) {
for (String value : entry.getValue()) {
@@ -94,15 +94,15 @@ public class OsmAndProtocolDecoder extends BaseHttpProtocolDecoder {
}
break;
case "lat":
- position.setLatitude(Double.parseDouble(value));
+ latitude = Double.parseDouble(value);
break;
case "lon":
- position.setLongitude(Double.parseDouble(value));
+ longitude = Double.parseDouble(value);
break;
case "location":
String[] location = value.split(",");
- position.setLatitude(Double.parseDouble(location[0]));
- position.setLongitude(Double.parseDouble(location[1]));
+ latitude = Double.parseDouble(location[0]);
+ longitude = Double.parseDouble(location[1]);
break;
case "cell":
String[] cell = value.split(",");
@@ -143,6 +143,9 @@ public class OsmAndProtocolDecoder extends BaseHttpProtocolDecoder {
case "driverUniqueId":
position.set(Position.KEY_DRIVER_UNIQUE_ID, value);
break;
+ case "charge":
+ position.set(Position.KEY_CHARGE, Boolean.parseBoolean(value));
+ break;
default:
try {
position.set(entry.getKey(), Double.parseDouble(value));
@@ -172,17 +175,17 @@ public class OsmAndProtocolDecoder extends BaseHttpProtocolDecoder {
position.setNetwork(network);
}
- if (position.getLatitude() == 0 && position.getLongitude() == 0) {
+ if (latitude != null && longitude != null) {
+ position.setLatitude(latitude);
+ position.setLongitude(longitude);
+ } else {
getLastLocation(position, position.getDeviceTime());
}
if (position.getDeviceId() != 0) {
String response = null;
- CommandsManager commandsManager = Context.getCommandsManager();
- if (commandsManager != null) {
- for (Command command : commandsManager.readQueuedCommands(position.getDeviceId(), 1)) {
- response = command.getString(Command.KEY_DATA);
- }
+ for (Command command : getCommandsManager().readQueuedCommands(position.getDeviceId(), 1)) {
+ response = command.getString(Command.KEY_DATA);
}
if (response != null) {
sendResponse(channel, HttpResponseStatus.OK, Unpooled.copiedBuffer(response, StandardCharsets.UTF_8));
diff --git a/src/main/java/org/traccar/protocol/OutsafeProtocol.java b/src/main/java/org/traccar/protocol/OutsafeProtocol.java
index c728a404d..0099be456 100644
--- a/src/main/java/org/traccar/protocol/OutsafeProtocol.java
+++ b/src/main/java/org/traccar/protocol/OutsafeProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class OutsafeProtocol extends BaseProtocol {
- public OutsafeProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public OutsafeProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new HttpResponseEncoder());
pipeline.addLast(new HttpRequestDecoder());
pipeline.addLast(new HttpObjectAggregator(65535));
diff --git a/src/main/java/org/traccar/protocol/OutsafeProtocolDecoder.java b/src/main/java/org/traccar/protocol/OutsafeProtocolDecoder.java
index 9de77d241..62b873be7 100644
--- a/src/main/java/org/traccar/protocol/OutsafeProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/OutsafeProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.channel.Channel;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import org.traccar.BaseHttpProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.model.Position;
diff --git a/src/main/java/org/traccar/protocol/OwnTracksProtocol.java b/src/main/java/org/traccar/protocol/OwnTracksProtocol.java
index 0086371d8..9ad337f19 100644
--- a/src/main/java/org/traccar/protocol/OwnTracksProtocol.java
+++ b/src/main/java/org/traccar/protocol/OwnTracksProtocol.java
@@ -22,13 +22,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class OwnTracksProtocol extends BaseProtocol {
- public OwnTracksProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public OwnTracksProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new HttpResponseEncoder());
pipeline.addLast(new HttpRequestDecoder());
pipeline.addLast(new HttpObjectAggregator(16384));
diff --git a/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java b/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java
index 509d14ae4..71ac87168 100644
--- a/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java
@@ -20,7 +20,7 @@ import io.netty.channel.Channel;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import org.traccar.BaseHttpProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
diff --git a/src/main/java/org/traccar/protocol/PacificTrackProtocol.java b/src/main/java/org/traccar/protocol/PacificTrackProtocol.java
index 08991ab64..709729ef1 100644
--- a/src/main/java/org/traccar/protocol/PacificTrackProtocol.java
+++ b/src/main/java/org/traccar/protocol/PacificTrackProtocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class PacificTrackProtocol extends BaseProtocol {
- public PacificTrackProtocol() {
- addServer(new TrackerServer(true, getName()) {
+ @Inject
+ public PacificTrackProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new PacificTrackProtocolDecoder(PacificTrackProtocol.this));
}
});
diff --git a/src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java
index b5d34a029..7079745be 100644
--- a/src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
diff --git a/src/main/java/org/traccar/protocol/PathAwayProtocol.java b/src/main/java/org/traccar/protocol/PathAwayProtocol.java
index 6b5d75c5e..1d13eea95 100644
--- a/src/main/java/org/traccar/protocol/PathAwayProtocol.java
+++ b/src/main/java/org/traccar/protocol/PathAwayProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class PathAwayProtocol extends BaseProtocol {
- public PathAwayProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public PathAwayProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new HttpResponseEncoder());
pipeline.addLast(new HttpRequestDecoder());
pipeline.addLast(new HttpObjectAggregator(16384));
diff --git a/src/main/java/org/traccar/protocol/PathAwayProtocolDecoder.java b/src/main/java/org/traccar/protocol/PathAwayProtocolDecoder.java
index 02a15e34a..3e7fa9a5b 100644
--- a/src/main/java/org/traccar/protocol/PathAwayProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/PathAwayProtocolDecoder.java
@@ -24,7 +24,7 @@ import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.QueryStringDecoder;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/PiligrimProtocol.java b/src/main/java/org/traccar/protocol/PiligrimProtocol.java
index d88c1ab72..aa45a0def 100644
--- a/src/main/java/org/traccar/protocol/PiligrimProtocol.java
+++ b/src/main/java/org/traccar/protocol/PiligrimProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class PiligrimProtocol extends BaseProtocol {
- public PiligrimProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public PiligrimProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new HttpResponseEncoder());
pipeline.addLast(new HttpRequestDecoder());
pipeline.addLast(new HttpObjectAggregator(16384));
diff --git a/src/main/java/org/traccar/protocol/PiligrimProtocolDecoder.java b/src/main/java/org/traccar/protocol/PiligrimProtocolDecoder.java
index 26ce2fe53..34c879cb8 100644
--- a/src/main/java/org/traccar/protocol/PiligrimProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/PiligrimProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,16 +22,19 @@ import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.QueryStringDecoder;
import org.traccar.BaseHttpProtocolDecoder;
-import org.traccar.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
import org.traccar.model.Position;
+import org.traccar.session.DeviceSession;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.LinkedList;
import java.util.List;
+import java.util.regex.Pattern;
public class PiligrimProtocolDecoder extends BaseHttpProtocolDecoder {
@@ -47,6 +50,21 @@ public class PiligrimProtocolDecoder extends BaseHttpProtocolDecoder {
public static final int MSG_GPS_SENSORS = 0xF2;
public static final int MSG_EVENTS = 0xF3;
+ private static final Pattern PATTERN = new PatternBuilder()
+ .expression("[^$]+")
+ .text("$GPRMC,")
+ .number("(dd)(dd)(dd).d+,") // time (hhmmss)
+ .expression("([AV]),") // validity
+ .number("(dd)(dd.d+),") // latitude
+ .expression("([NS]),")
+ .number("(d{2,3})(dd.d+),") // longitude
+ .expression("([EW]),")
+ .number("(d+.d+),") // speed
+ .number("(d+.d+),") // course
+ .number("(dd)(dd)(dd),") // date (ddmmyy)
+ .any()
+ .compile();
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -150,6 +168,42 @@ public class PiligrimProtocolDecoder extends BaseHttpProtocolDecoder {
}
return positions;
+
+ } else if (uri.startsWith("/push.do")) {
+
+ sendResponse(channel, "PUSH.DO: OK");
+
+ String sentence = request.content().toString(StandardCharsets.US_ASCII);
+
+ String[] parts = sentence.split("&");
+ String phone = parts[1].substring(16);
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, phone);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Parser parser = new Parser(PATTERN, parts[2]);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
+
+ position.setValid(parser.next().equals("A"));
+ position.setLatitude(parser.nextCoordinate());
+ position.setLongitude(parser.nextCoordinate());
+ position.setSpeed(parser.nextDouble());
+ position.setCourse(parser.nextDouble());
+
+ dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
+ position.setTime(dateBuilder.getDate());
+
+ return position;
+
}
return null;
diff --git a/src/main/java/org/traccar/protocol/PluginProtocol.java b/src/main/java/org/traccar/protocol/PluginProtocol.java
index d5f28da9d..b2101b18d 100644
--- a/src/main/java/org/traccar/protocol/PluginProtocol.java
+++ b/src/main/java/org/traccar/protocol/PluginProtocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class PluginProtocol extends BaseProtocol {
- public PluginProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public PluginProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '#'));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/PluginProtocolDecoder.java b/src/main/java/org/traccar/protocol/PluginProtocolDecoder.java
index 65de211ac..6ee95d18a 100644
--- a/src/main/java/org/traccar/protocol/PluginProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/PluginProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/PolteProtocol.java b/src/main/java/org/traccar/protocol/PolteProtocol.java
index a3e548716..69666cc0e 100644
--- a/src/main/java/org/traccar/protocol/PolteProtocol.java
+++ b/src/main/java/org/traccar/protocol/PolteProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class PolteProtocol extends BaseProtocol {
- public PolteProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public PolteProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new HttpResponseEncoder());
pipeline.addLast(new HttpRequestDecoder());
pipeline.addLast(new HttpObjectAggregator(65535));
diff --git a/src/main/java/org/traccar/protocol/PolteProtocolDecoder.java b/src/main/java/org/traccar/protocol/PolteProtocolDecoder.java
index ce45abef6..028de5424 100644
--- a/src/main/java/org/traccar/protocol/PolteProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/PolteProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.channel.Channel;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import org.traccar.BaseHttpProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.model.Position;
diff --git a/src/main/java/org/traccar/protocol/PortmanProtocol.java b/src/main/java/org/traccar/protocol/PortmanProtocol.java
index b7faae08a..de78013fa 100644
--- a/src/main/java/org/traccar/protocol/PortmanProtocol.java
+++ b/src/main/java/org/traccar/protocol/PortmanProtocol.java
@@ -21,17 +21,21 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class PortmanProtocol extends BaseProtocol {
- public PortmanProtocol() {
+ @Inject
+ public PortmanProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_ENGINE_STOP,
Command.TYPE_ENGINE_RESUME);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/PortmanProtocolDecoder.java b/src/main/java/org/traccar/protocol/PortmanProtocolDecoder.java
index e1847a2b2..da9403313 100644
--- a/src/main/java/org/traccar/protocol/PortmanProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/PortmanProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/PretraceProtocol.java b/src/main/java/org/traccar/protocol/PretraceProtocol.java
index 9d35c1c2f..b77dd97bf 100644
--- a/src/main/java/org/traccar/protocol/PretraceProtocol.java
+++ b/src/main/java/org/traccar/protocol/PretraceProtocol.java
@@ -21,17 +21,21 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class PretraceProtocol extends BaseProtocol {
- public PretraceProtocol() {
+ @Inject
+ public PretraceProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_CUSTOM,
Command.TYPE_POSITION_PERIODIC);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ')'));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/PretraceProtocolDecoder.java b/src/main/java/org/traccar/protocol/PretraceProtocolDecoder.java
index a19384e62..ff6ad763a 100644
--- a/src/main/java/org/traccar/protocol/PretraceProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/PretraceProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/PretraceProtocolEncoder.java b/src/main/java/org/traccar/protocol/PretraceProtocolEncoder.java
index 1083a252e..a109e7a07 100644
--- a/src/main/java/org/traccar/protocol/PretraceProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/PretraceProtocolEncoder.java
@@ -16,10 +16,9 @@
package org.traccar.protocol;
import org.traccar.BaseProtocolEncoder;
-import org.traccar.Context;
+import org.traccar.Protocol;
import org.traccar.helper.Checksum;
import org.traccar.model.Command;
-import org.traccar.Protocol;
public class PretraceProtocolEncoder extends BaseProtocolEncoder {
@@ -35,7 +34,7 @@ public class PretraceProtocolEncoder extends BaseProtocolEncoder {
@Override
protected Object encodeCommand(Command command) {
- String uniqueId = Context.getIdentityManager().getById(command.getDeviceId()).getUniqueId();
+ String uniqueId = getUniqueId(command.getDeviceId());
switch (command.getType()) {
case Command.TYPE_CUSTOM:
diff --git a/src/main/java/org/traccar/protocol/PricolProtocol.java b/src/main/java/org/traccar/protocol/PricolProtocol.java
index 6821cd949..f5e904541 100644
--- a/src/main/java/org/traccar/protocol/PricolProtocol.java
+++ b/src/main/java/org/traccar/protocol/PricolProtocol.java
@@ -19,20 +19,24 @@ import io.netty.handler.codec.FixedLengthFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class PricolProtocol extends BaseProtocol {
- public PricolProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public PricolProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new FixedLengthFrameDecoder(64));
pipeline.addLast(new PricolProtocolDecoder(PricolProtocol.this));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new PricolProtocolDecoder(PricolProtocol.this));
}
});
diff --git a/src/main/java/org/traccar/protocol/PricolProtocolDecoder.java b/src/main/java/org/traccar/protocol/PricolProtocolDecoder.java
index 190c68258..5f6805f09 100644
--- a/src/main/java/org/traccar/protocol/PricolProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/PricolProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
diff --git a/src/main/java/org/traccar/protocol/ProgressProtocol.java b/src/main/java/org/traccar/protocol/ProgressProtocol.java
index aac84205d..49eb6847f 100644
--- a/src/main/java/org/traccar/protocol/ProgressProtocol.java
+++ b/src/main/java/org/traccar/protocol/ProgressProtocol.java
@@ -19,14 +19,18 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import java.nio.ByteOrder;
+import javax.inject.Inject;
+
public class ProgressProtocol extends BaseProtocol {
- public ProgressProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public ProgressProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 2, 2, 4, 0, true));
pipeline.addLast(new ProgressProtocolDecoder(ProgressProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/ProgressProtocolDecoder.java b/src/main/java/org/traccar/protocol/ProgressProtocolDecoder.java
index 0025cd9e7..e3a5881da 100644
--- a/src/main/java/org/traccar/protocol/ProgressProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ProgressProtocolDecoder.java
@@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/PstProtocol.java b/src/main/java/org/traccar/protocol/PstProtocol.java
index d8c7008cb..73f978cbd 100644
--- a/src/main/java/org/traccar/protocol/PstProtocol.java
+++ b/src/main/java/org/traccar/protocol/PstProtocol.java
@@ -18,24 +18,28 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class PstProtocol extends BaseProtocol {
- public PstProtocol() {
+ @Inject
+ public PstProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_ENGINE_STOP,
Command.TYPE_ENGINE_RESUME);
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new PstProtocolEncoder(PstProtocol.this));
pipeline.addLast(new PstProtocolDecoder(PstProtocol.this));
}
});
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new PstFrameEncoder());
pipeline.addLast(new PstFrameDecoder());
pipeline.addLast(new PstProtocolEncoder(PstProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/PstProtocolDecoder.java b/src/main/java/org/traccar/protocol/PstProtocolDecoder.java
index e3fe1af62..872e77a3a 100644
--- a/src/main/java/org/traccar/protocol/PstProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/PstProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/Pt215Protocol.java b/src/main/java/org/traccar/protocol/Pt215Protocol.java
index 09bd9b121..b272582a4 100644
--- a/src/main/java/org/traccar/protocol/Pt215Protocol.java
+++ b/src/main/java/org/traccar/protocol/Pt215Protocol.java
@@ -19,13 +19,17 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class Pt215Protocol extends BaseProtocol {
- public Pt215Protocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public Pt215Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 2, 1, -1, 0));
pipeline.addLast(new Pt215ProtocolDecoder(Pt215Protocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/Pt215ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Pt215ProtocolDecoder.java
index 48ce7dede..f669c5ffd 100644
--- a/src/main/java/org/traccar/protocol/Pt215ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Pt215ProtocolDecoder.java
@@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/Pt3000Protocol.java b/src/main/java/org/traccar/protocol/Pt3000Protocol.java
index 1ad0026a3..d72774f47 100644
--- a/src/main/java/org/traccar/protocol/Pt3000Protocol.java
+++ b/src/main/java/org/traccar/protocol/Pt3000Protocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class Pt3000Protocol extends BaseProtocol {
- public Pt3000Protocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public Pt3000Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, 'd')); // probably wrong
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/Pt3000ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Pt3000ProtocolDecoder.java
index e7f9e062a..c33660f51 100644
--- a/src/main/java/org/traccar/protocol/Pt3000ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Pt3000ProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/Pt502Protocol.java b/src/main/java/org/traccar/protocol/Pt502Protocol.java
index 56444fb42..d5d30e8e8 100644
--- a/src/main/java/org/traccar/protocol/Pt502Protocol.java
+++ b/src/main/java/org/traccar/protocol/Pt502Protocol.java
@@ -19,20 +19,24 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class Pt502Protocol extends BaseProtocol {
- public Pt502Protocol() {
+ @Inject
+ public Pt502Protocol(Config config) {
setSupportedDataCommands(
Command.TYPE_CUSTOM,
Command.TYPE_SET_TIMEZONE,
Command.TYPE_ALARM_SPEED,
Command.TYPE_OUTPUT_CONTROL,
Command.TYPE_REQUEST_PHOTO);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new Pt502FrameDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new Pt502ProtocolEncoder(Pt502Protocol.this));
diff --git a/src/main/java/org/traccar/protocol/Pt502ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Pt502ProtocolDecoder.java
index ff92b51f1..2a6a81a65 100644
--- a/src/main/java/org/traccar/protocol/Pt502ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Pt502ProtocolDecoder.java
@@ -20,8 +20,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
@@ -175,14 +174,13 @@ public class Pt502ProtocolDecoder extends BaseProtocolDecoder {
} else {
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
- String uniqueId = Context.getIdentityManager().getById(deviceSession.getDeviceId()).getUniqueId();
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
getLastLocation(position, null);
- position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(uniqueId, photo, "jpg"));
+ position.set(Position.KEY_IMAGE, writeMediaFile(deviceSession.getUniqueId(), photo, "jpg"));
photo.release();
photo = null;
diff --git a/src/main/java/org/traccar/protocol/Pt60Protocol.java b/src/main/java/org/traccar/protocol/Pt60Protocol.java
index c502426c5..58345f025 100644
--- a/src/main/java/org/traccar/protocol/Pt60Protocol.java
+++ b/src/main/java/org/traccar/protocol/Pt60Protocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class Pt60Protocol extends BaseProtocol {
- public Pt60Protocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public Pt60Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "@R#@", "@E#@"));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/Pt60ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Pt60ProtocolDecoder.java
index 6a3fe2734..94b549fe6 100644
--- a/src/main/java/org/traccar/protocol/Pt60ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Pt60ProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/R12wProtocol.java b/src/main/java/org/traccar/protocol/R12wProtocol.java
index 3726233b4..a406f6306 100644
--- a/src/main/java/org/traccar/protocol/R12wProtocol.java
+++ b/src/main/java/org/traccar/protocol/R12wProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class R12wProtocol extends BaseProtocol {
- public R12wProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public R12wProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/R12wProtocolDecoder.java b/src/main/java/org/traccar/protocol/R12wProtocolDecoder.java
index d60318447..3be784911 100644
--- a/src/main/java/org/traccar/protocol/R12wProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/R12wProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Checksum;
diff --git a/src/main/java/org/traccar/protocol/RaceDynamicsProtocol.java b/src/main/java/org/traccar/protocol/RaceDynamicsProtocol.java
index c9db10610..63ca3476c 100644
--- a/src/main/java/org/traccar/protocol/RaceDynamicsProtocol.java
+++ b/src/main/java/org/traccar/protocol/RaceDynamicsProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class RaceDynamicsProtocol extends BaseProtocol {
- public RaceDynamicsProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public RaceDynamicsProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1500));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/RaceDynamicsProtocolDecoder.java b/src/main/java/org/traccar/protocol/RaceDynamicsProtocolDecoder.java
index f441bf8ed..89639ad30 100644
--- a/src/main/java/org/traccar/protocol/RaceDynamicsProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/RaceDynamicsProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
diff --git a/src/main/java/org/traccar/protocol/RadarProtocol.java b/src/main/java/org/traccar/protocol/RadarProtocol.java
index 9783778f0..9d88c6d72 100644
--- a/src/main/java/org/traccar/protocol/RadarProtocol.java
+++ b/src/main/java/org/traccar/protocol/RadarProtocol.java
@@ -19,13 +19,17 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class RadarProtocol extends BaseProtocol {
- public RadarProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public RadarProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 12, 2, -14, 0));
pipeline.addLast(new RadarProtocolDecoder(RadarProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/RadarProtocolDecoder.java b/src/main/java/org/traccar/protocol/RadarProtocolDecoder.java
index d87f77b84..818e97f8b 100644
--- a/src/main/java/org/traccar/protocol/RadarProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/RadarProtocolDecoder.java
@@ -18,7 +18,7 @@ package org.traccar.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.UnitsConverter;
diff --git a/src/main/java/org/traccar/protocol/RaveonProtocol.java b/src/main/java/org/traccar/protocol/RaveonProtocol.java
index 44faadb3b..db70396ee 100644
--- a/src/main/java/org/traccar/protocol/RaveonProtocol.java
+++ b/src/main/java/org/traccar/protocol/RaveonProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class RaveonProtocol extends BaseProtocol {
- public RaveonProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public RaveonProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/RaveonProtocolDecoder.java b/src/main/java/org/traccar/protocol/RaveonProtocolDecoder.java
index 50acd20a1..dfc21bf69 100644
--- a/src/main/java/org/traccar/protocol/RaveonProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/RaveonProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/RecodaProtocol.java b/src/main/java/org/traccar/protocol/RecodaProtocol.java
index 0bc9870bc..0d50db01e 100644
--- a/src/main/java/org/traccar/protocol/RecodaProtocol.java
+++ b/src/main/java/org/traccar/protocol/RecodaProtocol.java
@@ -19,14 +19,18 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import java.nio.ByteOrder;
+import javax.inject.Inject;
+
public class RecodaProtocol extends BaseProtocol {
- public RecodaProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public RecodaProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 4, 4, -8, 0, true));
pipeline.addLast(new RecodaProtocolDecoder(RecodaProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/RecodaProtocolDecoder.java b/src/main/java/org/traccar/protocol/RecodaProtocolDecoder.java
index 04098225f..0c417a62f 100644
--- a/src/main/java/org/traccar/protocol/RecodaProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/RecodaProtocolDecoder.java
@@ -18,7 +18,7 @@ package org.traccar.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.UnitsConverter;
diff --git a/src/main/java/org/traccar/protocol/RetranslatorProtocol.java b/src/main/java/org/traccar/protocol/RetranslatorProtocol.java
index fae81f7d2..1d4b419bb 100644
--- a/src/main/java/org/traccar/protocol/RetranslatorProtocol.java
+++ b/src/main/java/org/traccar/protocol/RetranslatorProtocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class RetranslatorProtocol extends BaseProtocol {
- public RetranslatorProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public RetranslatorProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new RetranslatorFrameDecoder());
pipeline.addLast(new RetranslatorProtocolDecoder(RetranslatorProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/RetranslatorProtocolDecoder.java b/src/main/java/org/traccar/protocol/RetranslatorProtocolDecoder.java
index 5bf6cef50..afbf7e511 100644
--- a/src/main/java/org/traccar/protocol/RetranslatorProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/RetranslatorProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.model.Position;
diff --git a/src/main/java/org/traccar/protocol/RfTrackProtocol.java b/src/main/java/org/traccar/protocol/RfTrackProtocol.java
new file mode 100644
index 000000000..d3b41e93e
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/RfTrackProtocol.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.http.HttpObjectAggregator;
+import io.netty.handler.codec.http.HttpRequestDecoder;
+import io.netty.handler.codec.http.HttpResponseEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
+
+public class RfTrackProtocol extends BaseProtocol {
+
+ @Inject
+ public RfTrackProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
+ pipeline.addLast(new HttpResponseEncoder());
+ pipeline.addLast(new HttpRequestDecoder());
+ pipeline.addLast(new HttpObjectAggregator(16384));
+ pipeline.addLast(new RfTrackProtocolDecoder(RfTrackProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/RfTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/RfTrackProtocolDecoder.java
new file mode 100644
index 000000000..28a3ac29c
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/RfTrackProtocolDecoder.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.HttpResponseStatus;
+import io.netty.handler.codec.http.QueryStringDecoder;
+import org.traccar.BaseHttpProtocolDecoder;
+import org.traccar.Protocol;
+import org.traccar.model.CellTower;
+import org.traccar.model.Command;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+import org.traccar.model.WifiAccessPoint;
+import org.traccar.session.DeviceSession;
+
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import java.io.StringReader;
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+public class RfTrackProtocolDecoder extends BaseHttpProtocolDecoder {
+
+ public RfTrackProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ FullHttpRequest request = (FullHttpRequest) msg;
+ QueryStringDecoder decoder = new QueryStringDecoder(
+ request.content().toString(StandardCharsets.US_ASCII), false);
+ Map<String, List<String>> params = decoder.parameters();
+
+ Position position = new Position(getProtocolName());
+ Network network = new Network();
+
+ for (Map.Entry<String, List<String>> entry : params.entrySet()) {
+ for (String value : entry.getValue()) {
+ switch (entry.getKey()) {
+ case "i":
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, value);
+ if (deviceSession == null) {
+ sendResponse(channel, HttpResponseStatus.BAD_REQUEST);
+ return null;
+ }
+ position.setDeviceId(deviceSession.getDeviceId());
+ break;
+ case "v":
+ position.set(Position.KEY_VERSION_FW, value);
+ break;
+ case "t":
+ position.setDeviceTime(new Date(Long.parseLong(value)));
+ break;
+ case "bat":
+ int battery = Integer.parseInt(value);
+ position.set(Position.KEY_BATTERY_LEVEL, battery & 0xff);
+ position.set("plugStatus", (battery >> 8) & 0x0f);
+ position.set(Position.KEY_CHARGE, ((battery >> 12) & 0x0f) == 1);
+ break;
+ case "id":
+ position.set("braceletId", value);
+ break;
+ case "rc":
+ int braceletCode = Integer.parseInt(value);
+ position.set("braceletCode", braceletCode & 0xffff);
+ position.set("braceletStatus", braceletCode >> 16);
+ break;
+ case "idt":
+ long braceletTime = Long.parseLong(value);
+ position.set("lastHeartbeat", (braceletTime >> 45) * 10);
+ position.set("lastPaired", ((braceletTime >> 30) & 0xffff) * 10);
+ position.set("lastUnpaired", ((braceletTime >> 15) & 0xffff) * 10);
+ break;
+ case "mt":
+ int vibrationTime = Integer.parseInt(value);
+ position.set("vibrationDevice", (vibrationTime & 0x7fff) * 10);
+ position.set("vibrationBracelet", (vibrationTime >> 15) * 10);
+ break;
+ case "gps":
+ JsonObject location = Json.createReader(new StringReader(value)).readObject();
+ position.setValid(true);
+ position.setAccuracy(location.getJsonNumber("a").doubleValue());
+ position.setLongitude(location.getJsonNumber("x").doubleValue());
+ position.setLatitude(location.getJsonNumber("y").doubleValue());
+ position.setAltitude(location.getJsonNumber("z").doubleValue());
+ position.setFixTime(new Date(location.getJsonNumber("t").longValue()));
+ break;
+ case "gsm":
+ JsonObject cellInfo = Json.createReader(new StringReader(value)).readObject();
+ int mcc = cellInfo.getInt("c");
+ int mnc = cellInfo.getInt("n");
+ JsonArray cells = cellInfo.getJsonArray("b");
+ for (int i = 0; i < cells.size(); i++) {
+ JsonObject cell = cells.getJsonObject(i);
+ network.addCellTower(CellTower.from(
+ mcc, mnc, cell.getInt("l"), cell.getInt("c"), cell.getInt("b")));
+ }
+ break;
+ case "dbm":
+ position.set(Position.KEY_RSSI, Integer.parseInt(value));
+ break;
+ case "bar":
+ position.set("pressure", Double.parseDouble(value));
+ break;
+ case "cob":
+ position.set("pressureChanges", value);
+ break;
+ case "wifi":
+ JsonArray wifiInfo = Json.createReader(new StringReader(value)).readArray();
+ for (int i = 0; i < wifiInfo.size(); i++) {
+ JsonObject wifi = wifiInfo.getJsonObject(i);
+ network.addWifiAccessPoint(WifiAccessPoint.from(
+ wifi.getString("m").replace('-', ':'), wifi.getInt("l")));
+ }
+ break;
+ case "u_ids":
+ position.set("unpairedIds", value);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (position.getFixTime() == null) {
+ getLastLocation(position, position.getDeviceTime());
+ }
+
+ if (network.getCellTowers() != null || network.getWifiAccessPoints() != null) {
+ position.setNetwork(network);
+ }
+
+ String response = "{}";
+ for (Command command : getCommandsManager().readQueuedCommands(position.getDeviceId(), 1)) {
+ response = command.getString(Command.KEY_DATA);
+ }
+ sendResponse(channel, HttpResponseStatus.OK, Unpooled.copiedBuffer(response, StandardCharsets.UTF_8));
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/RitiProtocol.java b/src/main/java/org/traccar/protocol/RitiProtocol.java
index de1026672..9b9c00cb2 100644
--- a/src/main/java/org/traccar/protocol/RitiProtocol.java
+++ b/src/main/java/org/traccar/protocol/RitiProtocol.java
@@ -19,14 +19,18 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import java.nio.ByteOrder;
+import javax.inject.Inject;
+
public class RitiProtocol extends BaseProtocol {
- public RitiProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public RitiProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 105, 2, 3, 0, true));
pipeline.addLast(new RitiProtocolDecoder(RitiProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/RitiProtocolDecoder.java b/src/main/java/org/traccar/protocol/RitiProtocolDecoder.java
index 46267ca90..501d5faa7 100644
--- a/src/main/java/org/traccar/protocol/RitiProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/RitiProtocolDecoder.java
@@ -18,7 +18,7 @@ package org.traccar.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/RoboTrackProtocol.java b/src/main/java/org/traccar/protocol/RoboTrackProtocol.java
index c2c531293..ab2bc5842 100644
--- a/src/main/java/org/traccar/protocol/RoboTrackProtocol.java
+++ b/src/main/java/org/traccar/protocol/RoboTrackProtocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class RoboTrackProtocol extends BaseProtocol {
- public RoboTrackProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public RoboTrackProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new RoboTrackFrameDecoder());
pipeline.addLast(new RoboTrackProtocolDecoder(RoboTrackProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/RoboTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/RoboTrackProtocolDecoder.java
index b613f31d7..ffe16bd7b 100644
--- a/src/main/java/org/traccar/protocol/RoboTrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/RoboTrackProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/RstProtocol.java b/src/main/java/org/traccar/protocol/RstProtocol.java
index 10d11d493..109d91b16 100644
--- a/src/main/java/org/traccar/protocol/RstProtocol.java
+++ b/src/main/java/org/traccar/protocol/RstProtocol.java
@@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class RstProtocol extends BaseProtocol {
- public RstProtocol() {
- addServer(new TrackerServer(true, getName()) {
+ @Inject
+ public RstProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new RstProtocolDecoder(RstProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/RstProtocolDecoder.java b/src/main/java/org/traccar/protocol/RstProtocolDecoder.java
index 9e3261a04..fcc96fbf1 100644
--- a/src/main/java/org/traccar/protocol/RstProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/RstProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/RuptelaProtocol.java b/src/main/java/org/traccar/protocol/RuptelaProtocol.java
index 5d1f86553..99a9686f6 100644
--- a/src/main/java/org/traccar/protocol/RuptelaProtocol.java
+++ b/src/main/java/org/traccar/protocol/RuptelaProtocol.java
@@ -19,11 +19,15 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class RuptelaProtocol extends BaseProtocol {
- public RuptelaProtocol() {
+ @Inject
+ public RuptelaProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_CUSTOM,
Command.TYPE_ENGINE_STOP,
@@ -35,9 +39,9 @@ public class RuptelaProtocol extends BaseProtocol {
Command.TYPE_OUTPUT_CONTROL,
Command.TYPE_SET_CONNECTION,
Command.TYPE_SET_ODOMETER);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 0, 2, 2, 0));
pipeline.addLast(new RuptelaProtocolEncoder(RuptelaProtocol.this));
pipeline.addLast(new RuptelaProtocolDecoder(RuptelaProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java b/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java
index 5c2885a8b..77df0deb7 100644
--- a/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,8 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.DataConverter;
@@ -50,6 +49,7 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_SMS_VIA_GPRS_RESPONSE = 7;
public static final int MSG_SMS_VIA_GPRS = 8;
public static final int MSG_DTCS = 9;
+ public static final int MSG_IDENTIFICATION = 15;
public static final int MSG_SET_IO = 17;
public static final int MSG_FILES = 37;
public static final int MSG_EXTENDED_RECORDS = 68;
@@ -102,6 +102,12 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder {
case 5:
position.set(Position.KEY_IGNITION, readValue(buf, length, false) == 1);
break;
+ case 29:
+ position.set(Position.KEY_POWER, readValue(buf, length, false));
+ break;
+ case 30:
+ position.set(Position.KEY_BATTERY, readValue(buf, length, false) * 0.001);
+ break;
case 74:
position.set(Position.PREFIX_TEMP + 3, readValue(buf, length, true) * 0.1);
break;
@@ -110,22 +116,19 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder {
case 80:
position.set(Position.PREFIX_TEMP + (id - 78), readValue(buf, length, true) * 0.1);
break;
- case 198:
- if (readValue(buf, length, false) > 0) {
- position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED);
- }
- break;
- case 199:
- case 200:
+ case 134:
if (readValue(buf, length, false) > 0) {
position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
}
break;
- case 201:
+ case 136:
if (readValue(buf, length, false) > 0) {
position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
}
break;
+ case 197:
+ position.set(Position.KEY_RPM, readValue(buf, length, false) * 0.125);
+ break;
default:
position.set(Position.PREFIX_IO + id, readValue(buf, length, false));
break;
@@ -294,7 +297,7 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
getLastLocation(position, null);
- position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(imei, photo, "jpg"));
+ position.set(Position.KEY_IMAGE, writeMediaFile(imei, photo, "jpg"));
photo.release();
photo = null;
return position;
@@ -303,6 +306,18 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder {
return null;
+ } else if (type == MSG_IDENTIFICATION) {
+
+ ByteBuf content = Unpooled.buffer();
+ content.writeByte(1);
+ ByteBuf response = RuptelaProtocolEncoder.encodeContent(type, content);
+ content.release();
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+
+ return null;
+
} else {
return decodeCommandResponse(deviceSession, type, buf);
diff --git a/src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java b/src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java
index 442961b19..5ec971a98 100644
--- a/src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java
@@ -80,14 +80,14 @@ public class RuptelaProtocolEncoder extends BaseProtocolEncoder {
return encodeContent(RuptelaProtocolDecoder.MSG_FIRMWARE_UPDATE, content);
case Command.TYPE_OUTPUT_CONTROL:
content.writeInt(command.getInteger(Command.KEY_INDEX));
- content.writeInt(Integer.parseInt(command.getString(Command.KEY_DATA)));
+ content.writeInt(command.getInteger(Command.KEY_DATA));
return encodeContent(RuptelaProtocolDecoder.MSG_SET_IO, content);
case Command.TYPE_SET_CONNECTION:
String c = command.getString(Command.KEY_SERVER) + "," + command.getInteger(Command.KEY_PORT) + ",TCP";
content.writeBytes(c.getBytes(StandardCharsets.US_ASCII));
return encodeContent(RuptelaProtocolDecoder.MSG_SET_CONNECTION, content);
case Command.TYPE_SET_ODOMETER:
- content.writeInt(Integer.parseInt(command.getString(Command.KEY_DATA)));
+ content.writeInt(command.getInteger(Command.KEY_DATA));
return encodeContent(RuptelaProtocolDecoder.MSG_SET_ODOMETER, content);
default:
return null;
diff --git a/src/main/java/org/traccar/protocol/S168Protocol.java b/src/main/java/org/traccar/protocol/S168Protocol.java
index e78664c40..f904ed9ff 100644
--- a/src/main/java/org/traccar/protocol/S168Protocol.java
+++ b/src/main/java/org/traccar/protocol/S168Protocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class S168Protocol extends BaseProtocol {
- public S168Protocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public S168Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '$'));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/S168ProtocolDecoder.java b/src/main/java/org/traccar/protocol/S168ProtocolDecoder.java
index 71aff1a65..cf665c6ba 100644
--- a/src/main/java/org/traccar/protocol/S168ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/S168ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,10 +17,13 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.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.text.DateFormat;
@@ -48,9 +51,14 @@ public class S168ProtocolDecoder extends BaseProtocolDecoder {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
+ Network network = new Network();
+
String content = values[4];
String[] fragments = content.split(";");
for (String fragment : fragments) {
+ if (fragment.isEmpty()) {
+ continue;
+ }
int dataIndex = fragment.indexOf(':');
String type = fragment.substring(0, dataIndex);
@@ -70,12 +78,44 @@ public class S168ProtocolDecoder extends BaseProtocolDecoder {
position.setCourse(Integer.parseInt(values[index++]));
position.setAltitude(Integer.parseInt(values[index++]));
break;
+ case "CELL":
+ int cellCount = Integer.parseInt(values[index++]);
+ int mcc = Integer.parseInt(values[index++], 16);
+ int mnc = Integer.parseInt(values[index++], 16);
+ for (int i = 0; i < cellCount; i++) {
+ network.addCellTower(CellTower.from(
+ mcc, mnc, Integer.parseInt(values[index++], 16), Integer.parseInt(values[index++], 16),
+ Integer.parseInt(values[index++], 16)));
+ }
+ break;
+ case "WIFI":
+ int wifiCount = Integer.parseInt(values[index++]);
+ for (int i = 0; i < wifiCount; i++) {
+ network.addWifiAccessPoint(WifiAccessPoint.from(
+ values[index++].replace('-', ':'), Integer.parseInt(values[index++])));
+ }
+ break;
+ case "STATUS":
+ position.set(Position.KEY_BATTERY_LEVEL, Integer.parseInt(values[index++]));
+ position.set(Position.KEY_RSSI, Integer.parseInt(values[index++]));
+ break;
default:
break;
}
}
- return position.getAttributes().containsKey(Position.KEY_SATELLITES) ? position : null;
+ if (network.getCellTowers() != null || network.getWifiAccessPoints() != null) {
+ position.setNetwork(network);
+ }
+ if (!position.hasAttribute(Position.KEY_SATELLITES)) {
+ getLastLocation(position, null);
+ }
+
+ if (position.getNetwork() != null || !position.getAttributes().isEmpty()) {
+ return position;
+ } else {
+ return null;
+ }
}
}
diff --git a/src/main/java/org/traccar/protocol/SabertekProtocol.java b/src/main/java/org/traccar/protocol/SabertekProtocol.java
index 0ec847b60..403243cdc 100644
--- a/src/main/java/org/traccar/protocol/SabertekProtocol.java
+++ b/src/main/java/org/traccar/protocol/SabertekProtocol.java
@@ -19,13 +19,17 @@ import io.netty.handler.codec.string.StringDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class SabertekProtocol extends BaseProtocol {
- public SabertekProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public SabertekProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new SabertekFrameDecoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new SabertekProtocolDecoder(SabertekProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/SabertekProtocolDecoder.java b/src/main/java/org/traccar/protocol/SabertekProtocolDecoder.java
index 3033aa2cc..71279812c 100644
--- a/src/main/java/org/traccar/protocol/SabertekProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SabertekProtocolDecoder.java
@@ -18,7 +18,7 @@ package org.traccar.protocol;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/SanavProtocol.java b/src/main/java/org/traccar/protocol/SanavProtocol.java
index 6799c57e6..1a0e7b0e9 100644
--- a/src/main/java/org/traccar/protocol/SanavProtocol.java
+++ b/src/main/java/org/traccar/protocol/SanavProtocol.java
@@ -20,21 +20,25 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class SanavProtocol extends BaseProtocol {
- public SanavProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public SanavProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new SanavProtocolDecoder(SanavProtocol.this));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new SanavProtocolDecoder(SanavProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/SanavProtocolDecoder.java b/src/main/java/org/traccar/protocol/SanavProtocolDecoder.java
index 7e1c158e6..6741cb67c 100644
--- a/src/main/java/org/traccar/protocol/SanavProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SanavProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
diff --git a/src/main/java/org/traccar/protocol/SanulProtocol.java b/src/main/java/org/traccar/protocol/SanulProtocol.java
index 3104e9366..ea44bf868 100644
--- a/src/main/java/org/traccar/protocol/SanulProtocol.java
+++ b/src/main/java/org/traccar/protocol/SanulProtocol.java
@@ -19,15 +19,19 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import java.nio.ByteOrder;
+import javax.inject.Inject;
+
public class SanulProtocol extends BaseProtocol {
- public SanulProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public SanulProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 3, 2, 0, 0, true));
pipeline.addLast(new SanulProtocolDecoder(SanulProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/SanulProtocolDecoder.java b/src/main/java/org/traccar/protocol/SanulProtocolDecoder.java
index 036d1ee51..9568cd6d3 100644
--- a/src/main/java/org/traccar/protocol/SanulProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SanulProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.model.Position;
diff --git a/src/main/java/org/traccar/protocol/SatsolProtocol.java b/src/main/java/org/traccar/protocol/SatsolProtocol.java
index b69fdd1fe..d90033e38 100644
--- a/src/main/java/org/traccar/protocol/SatsolProtocol.java
+++ b/src/main/java/org/traccar/protocol/SatsolProtocol.java
@@ -19,15 +19,19 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import java.nio.ByteOrder;
+import javax.inject.Inject;
+
public class SatsolProtocol extends BaseProtocol {
- public SatsolProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public SatsolProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1400, 8, 2, 0, 0, true));
pipeline.addLast(new SatsolProtocolDecoder(SatsolProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/SatsolProtocolDecoder.java b/src/main/java/org/traccar/protocol/SatsolProtocolDecoder.java
index c457d5620..37a84be04 100644
--- a/src/main/java/org/traccar/protocol/SatsolProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SatsolProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/SigfoxProtocol.java b/src/main/java/org/traccar/protocol/SigfoxProtocol.java
index e2f2cbe1f..9a268af62 100644
--- a/src/main/java/org/traccar/protocol/SigfoxProtocol.java
+++ b/src/main/java/org/traccar/protocol/SigfoxProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class SigfoxProtocol extends BaseProtocol {
- public SigfoxProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public SigfoxProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new HttpResponseEncoder());
pipeline.addLast(new HttpRequestDecoder());
pipeline.addLast(new HttpObjectAggregator(65535));
diff --git a/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java b/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java
index ce577f392..4ed2bb51d 100644
--- a/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java
@@ -22,7 +22,8 @@ import io.netty.channel.Channel;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import org.traccar.BaseHttpProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.helper.BufferUtil;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DataConverter;
@@ -159,18 +160,8 @@ public class SigfoxProtocolDecoder extends BaseHttpProtocolDecoder {
if (event == 0x0f || event == 0x1f) {
position.setValid(event >> 4 > 0);
-
- long value;
- value = buf.readUnsignedInt();
- position.setLatitude(BitUtil.to(value, 31) * 0.000001);
- if (BitUtil.check(value, 31)) {
- position.setLatitude(-position.getLatitude());
- }
- value = buf.readUnsignedInt();
- position.setLongitude(BitUtil.to(value, 31) * 0.000001);
- if (BitUtil.check(value, 31)) {
- position.setLongitude(-position.getLongitude());
- }
+ position.setLatitude(BufferUtil.readSignedMagnitudeInt(buf) * 0.000001);
+ position.setLongitude(BufferUtil.readSignedMagnitudeInt(buf) * 0.000001);
position.set(Position.KEY_BATTERY, (int) buf.readUnsignedByte());
diff --git a/src/main/java/org/traccar/protocol/SiwiProtocol.java b/src/main/java/org/traccar/protocol/SiwiProtocol.java
index 8963721c8..f12958a50 100644
--- a/src/main/java/org/traccar/protocol/SiwiProtocol.java
+++ b/src/main/java/org/traccar/protocol/SiwiProtocol.java
@@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class SiwiProtocol extends BaseProtocol {
- public SiwiProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public SiwiProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new SiwiProtocolDecoder(SiwiProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/SiwiProtocolDecoder.java b/src/main/java/org/traccar/protocol/SiwiProtocolDecoder.java
index bf8bfab77..7ba501834 100644
--- a/src/main/java/org/traccar/protocol/SiwiProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SiwiProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/SkypatrolProtocol.java b/src/main/java/org/traccar/protocol/SkypatrolProtocol.java
index 7c6203d86..7ae26f634 100644
--- a/src/main/java/org/traccar/protocol/SkypatrolProtocol.java
+++ b/src/main/java/org/traccar/protocol/SkypatrolProtocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class SkypatrolProtocol extends BaseProtocol {
- public SkypatrolProtocol() {
- addServer(new TrackerServer(true, getName()) {
+ @Inject
+ public SkypatrolProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new SkypatrolProtocolDecoder(SkypatrolProtocol.this));
}
});
diff --git a/src/main/java/org/traccar/protocol/SkypatrolProtocolDecoder.java b/src/main/java/org/traccar/protocol/SkypatrolProtocolDecoder.java
index 8aae310bb..6ffcbbe44 100644
--- a/src/main/java/org/traccar/protocol/SkypatrolProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SkypatrolProtocolDecoder.java
@@ -20,8 +20,7 @@ import io.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.config.Keys;
import org.traccar.helper.BitUtil;
@@ -35,11 +34,15 @@ public class SkypatrolProtocolDecoder extends BaseProtocolDecoder {
private static final Logger LOGGER = LoggerFactory.getLogger(SkypatrolProtocolDecoder.class);
- private final long defaultMask;
+ private long defaultMask;
public SkypatrolProtocolDecoder(Protocol protocol) {
super(protocol);
- defaultMask = Context.getConfig().getInteger(Keys.PROTOCOL_MASK.withPrefix(getProtocolName()));
+ }
+
+ @Override
+ protected void init() {
+ defaultMask = getConfig().getInteger(Keys.PROTOCOL_MASK.withPrefix(getProtocolName()));
}
private static double convertCoordinate(long coordinate) {
diff --git a/src/main/java/org/traccar/protocol/SmartSoleProtocol.java b/src/main/java/org/traccar/protocol/SmartSoleProtocol.java
index bcf43f68b..cb7efb7ee 100644
--- a/src/main/java/org/traccar/protocol/SmartSoleProtocol.java
+++ b/src/main/java/org/traccar/protocol/SmartSoleProtocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class SmartSoleProtocol extends BaseProtocol {
- public SmartSoleProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public SmartSoleProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '$'));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/SmartSoleProtocolDecoder.java b/src/main/java/org/traccar/protocol/SmartSoleProtocolDecoder.java
index 04920c969..7fc38f061 100644
--- a/src/main/java/org/traccar/protocol/SmartSoleProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SmartSoleProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/SmokeyProtocol.java b/src/main/java/org/traccar/protocol/SmokeyProtocol.java
index 482c8347c..22b343537 100644
--- a/src/main/java/org/traccar/protocol/SmokeyProtocol.java
+++ b/src/main/java/org/traccar/protocol/SmokeyProtocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class SmokeyProtocol extends BaseProtocol {
- public SmokeyProtocol() {
- addServer(new TrackerServer(true, getName()) {
+ @Inject
+ public SmokeyProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new SmokeyProtocolDecoder(SmokeyProtocol.this));
}
});
diff --git a/src/main/java/org/traccar/protocol/SmokeyProtocolDecoder.java b/src/main/java/org/traccar/protocol/SmokeyProtocolDecoder.java
index 9da52e97a..2244ad289 100644
--- a/src/main/java/org/traccar/protocol/SmokeyProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SmokeyProtocolDecoder.java
@@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
diff --git a/src/main/java/org/traccar/protocol/SolarPoweredProtocol.java b/src/main/java/org/traccar/protocol/SolarPoweredProtocol.java
index 53a948cdc..0676aa629 100644
--- a/src/main/java/org/traccar/protocol/SolarPoweredProtocol.java
+++ b/src/main/java/org/traccar/protocol/SolarPoweredProtocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class SolarPoweredProtocol extends BaseProtocol {
- public SolarPoweredProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public SolarPoweredProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new HuabaoFrameDecoder());
pipeline.addLast(new SolarPoweredProtocolDecoder(SolarPoweredProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/SolarPoweredProtocolDecoder.java b/src/main/java/org/traccar/protocol/SolarPoweredProtocolDecoder.java
index 9d5dc072f..0432fbd03 100644
--- a/src/main/java/org/traccar/protocol/SolarPoweredProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SolarPoweredProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
diff --git a/src/main/java/org/traccar/protocol/SpotProtocol.java b/src/main/java/org/traccar/protocol/SpotProtocol.java
index bbf0e8d8a..6bd802fed 100644
--- a/src/main/java/org/traccar/protocol/SpotProtocol.java
+++ b/src/main/java/org/traccar/protocol/SpotProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.http.HttpResponseEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class SpotProtocol extends BaseProtocol {
- public SpotProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public SpotProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new HttpResponseEncoder());
pipeline.addLast(new HttpRequestDecoder());
pipeline.addLast(new HttpObjectAggregator(65535));
diff --git a/src/main/java/org/traccar/protocol/SpotProtocolDecoder.java b/src/main/java/org/traccar/protocol/SpotProtocolDecoder.java
index 34417d95f..d493b748d 100644
--- a/src/main/java/org/traccar/protocol/SpotProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SpotProtocolDecoder.java
@@ -20,7 +20,7 @@ import io.netty.channel.Channel;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import org.traccar.BaseHttpProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.DateUtil;
import org.traccar.model.Position;
diff --git a/src/main/java/org/traccar/protocol/StarLinkProtocol.java b/src/main/java/org/traccar/protocol/StarLinkProtocol.java
index 5630722ee..d578fa705 100644
--- a/src/main/java/org/traccar/protocol/StarLinkProtocol.java
+++ b/src/main/java/org/traccar/protocol/StarLinkProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class StarLinkProtocol extends BaseProtocol {
- public StarLinkProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public StarLinkProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java b/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java
index 7a6b6f4fe..aa23bfac5 100644
--- a/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,9 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
+import org.traccar.config.Keys;
+import org.traccar.helper.model.AttributeUtil;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.DataConverter;
import org.traccar.helper.Parser;
@@ -55,17 +56,21 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
public StarLinkProtocolDecoder(Protocol protocol) {
super(protocol);
+ }
- setFormat(Context.getConfig().getString(
+ @Override
+ protected void init() {
+ setFormat(getConfig().getString(
getProtocolName() + ".format", "#EDT#,#EID#,#PDT#,#LAT#,#LONG#,#SPD#,#HEAD#,#ODO#,"
+ "#IN1#,#IN2#,#IN3#,#IN4#,#OUT1#,#OUT2#,#OUT3#,#OUT4#,#LAC#,#CID#,#VIN#,#VBAT#,#DEST#,#IGN#,#ENG#"));
- setDateFormat(Context.getConfig().getString(getProtocolName() + ".dateFormat", "yyMMddHHmmss"));
+ setDateFormat(getConfig().getString(getProtocolName() + ".dateFormat", "yyMMddHHmmss"));
}
public String[] getFormat(long deviceId) {
- return Context.getIdentityManager().lookupAttributeString(
- deviceId, getProtocolName() + ".format", format, false, false).split(",");
+ String value = AttributeUtil.lookup(
+ getCacheManager(), Keys.PROTOCOL_FORMAT.withPrefix(getProtocolName()), deviceId);
+ return (value != null ? value : format).split(",");
}
public void setFormat(String format) {
@@ -73,8 +78,9 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
}
public DateFormat getDateFormat(long deviceId) {
- DateFormat dateFormat = new SimpleDateFormat(Context.getIdentityManager().lookupAttributeString(
- deviceId, getProtocolName() + ".dateFormat", this.dateFormat, false, false));
+ String value = AttributeUtil.lookup(
+ getCacheManager(), Keys.PROTOCOL_DATE_FORMAT.withPrefix(getProtocolName()), deviceId);
+ DateFormat dateFormat = new SimpleDateFormat(value != null ? value : this.dateFormat);
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
return dateFormat;
}
@@ -313,7 +319,7 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
}
if (lac != null && cid != null) {
- position.setNetwork(new Network(CellTower.fromLacCid(lac, cid)));
+ position.setNetwork(new Network(CellTower.fromLacCid(getConfig(), lac, cid)));
}
if (event == 20) {
diff --git a/src/main/java/org/traccar/protocol/StarcomProtocol.java b/src/main/java/org/traccar/protocol/StarcomProtocol.java
index 63a6143a6..33c3a4776 100644
--- a/src/main/java/org/traccar/protocol/StarcomProtocol.java
+++ b/src/main/java/org/traccar/protocol/StarcomProtocol.java
@@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class StarcomProtocol extends BaseProtocol {
- public StarcomProtocol() {
- addServer(new TrackerServer(true, getName()) {
+ @Inject
+ public StarcomProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StarcomProtocolDecoder(StarcomProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/StarcomProtocolDecoder.java b/src/main/java/org/traccar/protocol/StarcomProtocolDecoder.java
index 5ffddb318..56ab733c8 100644
--- a/src/main/java/org/traccar/protocol/StarcomProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/StarcomProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
diff --git a/src/main/java/org/traccar/protocol/StartekProtocol.java b/src/main/java/org/traccar/protocol/StartekProtocol.java
index 32f1c5a29..d010df858 100644
--- a/src/main/java/org/traccar/protocol/StartekProtocol.java
+++ b/src/main/java/org/traccar/protocol/StartekProtocol.java
@@ -21,19 +21,23 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class StartekProtocol extends BaseProtocol {
- public StartekProtocol() {
+ @Inject
+ public StartekProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_CUSTOM,
Command.TYPE_OUTPUT_CONTROL,
Command.TYPE_ENGINE_STOP,
Command.TYPE_ENGINE_RESUME);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java b/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java
index 042518cb2..8e3624cb5 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 Anton Tananaev (anton@traccar.org)
+ * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.Parser;
@@ -70,17 +70,30 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder {
.number("(x+),") // outputs
.number("(x+)|") // power
.number("(x+)") // battery
- .groupBegin()
- .text("|")
- .expression("([^,]+)").optional() // adc
+ .expression("([^,]+)?") // adc
.groupBegin()
.text(",")
.number("d,") // extended
- .expression("([^,]+)?,") // fuel
- .expression("([^,]+)?,?") // temperature
+ .expression("([^,]+)?") // fuel
+ .groupBegin()
+ .text(",")
+ .expression("([^,]+)?") // temperature
+ .groupBegin()
+ .text(",")
+ .groupBegin()
+ .number("(d+)?|") // rpm
+ .number("(d+)?|") // engine load
+ .number("(d+)?|") // maf flow
+ .number("(d+)?|") // intake pressure
+ .number("(d+)?|") // intake temperature
+ .number("(d+)?|") // throttle
+ .number("(d+)?|") // coolant temperature
+ .number("(d+)?|") // instant fuel
+ .number("(d+)[%L]").optional() // fuel level
+ .groupEnd("?")
+ .groupEnd("?")
.groupEnd("?")
.groupEnd("?")
- .any()
.compile();
private String decodeAlarm(int value) {
@@ -181,7 +194,7 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder {
if (parser.hasNext()) {
String[] adc = parser.next().split("\\|");
- for (int i = 0; i < adc.length; i++) {
+ for (int i = 1; i < adc.length; i++) {
position.set(Position.PREFIX_ADC + (i + 1), Integer.parseInt(adc[i], 16) * 0.01);
}
}
@@ -208,6 +221,24 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder {
}
}
+ if (parser.hasNext(6)) {
+ position.set(Position.KEY_RPM, parser.nextInt());
+ position.set(Position.KEY_ENGINE_LOAD, parser.nextInt());
+ position.set("airFlow", parser.nextInt());
+ position.set("airPressure", parser.nextInt());
+ if (parser.hasNext()) {
+ position.set("airTemp", parser.nextInt() - 40);
+ }
+ position.set(Position.KEY_THROTTLE, parser.nextInt());
+ if (parser.hasNext()) {
+ position.set(Position.KEY_COOLANT_TEMP, parser.nextInt() - 40);
+ }
+ if (parser.hasNext()) {
+ position.set(Position.KEY_FUEL_CONSUMPTION, parser.nextInt() * 0.1);
+ }
+ position.set(Position.KEY_FUEL_LEVEL, parser.nextInt());
+ }
+
return position;
}
diff --git a/src/main/java/org/traccar/protocol/StbProtocol.java b/src/main/java/org/traccar/protocol/StbProtocol.java
index 002ed86c7..af4e0d2c4 100644
--- a/src/main/java/org/traccar/protocol/StbProtocol.java
+++ b/src/main/java/org/traccar/protocol/StbProtocol.java
@@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class StbProtocol extends BaseProtocol {
- public StbProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public StbProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new JsonFrameDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/StbProtocolDecoder.java b/src/main/java/org/traccar/protocol/StbProtocolDecoder.java
index cc985d605..641359bfd 100644
--- a/src/main/java/org/traccar/protocol/StbProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/StbProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,15 +15,12 @@
*/
package org.traccar.protocol;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.core.JsonProcessingException;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.model.Position;
+import org.traccar.session.DeviceSession;
import javax.json.Json;
import javax.json.JsonObject;
@@ -42,29 +39,13 @@ public class StbProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_PROPERTY = 310;
public static final int MSG_ALARM = 410;
- public static class Response {
- @JsonProperty("msgType")
- private int type;
- @JsonProperty("devId")
- private String deviceId;
- @JsonProperty("result")
- private int result;
- @JsonProperty("txnNo")
- private String transaction;
- }
-
private void sendResponse(
- Channel channel, SocketAddress remoteAddress, int type, String deviceId, JsonObject root)
- throws JsonProcessingException {
-
- Response response = new Response();
- response.type = type + 1;
- response.deviceId = deviceId;
- response.result = 1;
- response.transaction = root.getString("txnNo");
+ Channel channel, SocketAddress remoteAddress, int type, String deviceId, JsonObject root) {
+ String response = String.format(
+ "{ \"msgType\": %d, \"devId\": \"%s\", \"result\": 1, \"txnNo\": \"%s\" }",
+ type + 1, deviceId, root.getString("txnNo"));
if (channel != null) {
- channel.writeAndFlush(new NetworkMessage(
- Context.getObjectMapper().writeValueAsString(response), remoteAddress));
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
}
diff --git a/src/main/java/org/traccar/protocol/Stl060Protocol.java b/src/main/java/org/traccar/protocol/Stl060Protocol.java
index 2711e936b..83b5db3bb 100644
--- a/src/main/java/org/traccar/protocol/Stl060Protocol.java
+++ b/src/main/java/org/traccar/protocol/Stl060Protocol.java
@@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class Stl060Protocol extends BaseProtocol {
- public Stl060Protocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public Stl060Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new Stl060FrameDecoder(1024));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/Stl060ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Stl060ProtocolDecoder.java
index 7b0055aa1..dc1fa3ba3 100644
--- a/src/main/java/org/traccar/protocol/Stl060ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Stl060ProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/SuntechProtocol.java b/src/main/java/org/traccar/protocol/SuntechProtocol.java
index 199885537..4253b761b 100644
--- a/src/main/java/org/traccar/protocol/SuntechProtocol.java
+++ b/src/main/java/org/traccar/protocol/SuntechProtocol.java
@@ -19,11 +19,15 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class SuntechProtocol extends BaseProtocol {
- public SuntechProtocol() {
+ @Inject
+ public SuntechProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_OUTPUT_CONTROL,
Command.TYPE_REBOOT_DEVICE,
@@ -32,9 +36,9 @@ public class SuntechProtocol extends BaseProtocol {
Command.TYPE_ENGINE_RESUME,
Command.TYPE_ALARM_ARM,
Command.TYPE_ALARM_DISARM);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new SuntechFrameDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new SuntechProtocolEncoder(SuntechProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
index 2d00ea81e..047a1822a 100644
--- a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,10 +17,13 @@ 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.Context;
-import org.traccar.DeviceSession;
+import org.traccar.config.Keys;
+import org.traccar.helper.BufferUtil;
+import org.traccar.helper.model.AttributeUtil;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
@@ -34,10 +37,17 @@ import java.nio.charset.StandardCharsets;
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.LinkedList;
+import java.util.List;
import java.util.TimeZone;
+import java.util.stream.Collectors;
public class SuntechProtocolDecoder extends BaseProtocolDecoder {
+ private boolean universal;
private String prefix;
private int protocolType;
@@ -46,10 +56,16 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
private boolean includeRpm;
private boolean includeTemp;
+ private ByteBuf crash;
+
public SuntechProtocolDecoder(Protocol protocol) {
super(protocol);
}
+ public boolean getUniversal() {
+ return universal;
+ }
+
public String getPrefix() {
return prefix;
}
@@ -59,8 +75,8 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
}
public int getProtocolType(long deviceId) {
- return Context.getIdentityManager().lookupAttributeInteger(
- deviceId, getProtocolName() + ".protocolType", protocolType, false, true);
+ Integer value = AttributeUtil.lookup(getCacheManager(), Keys.PROTOCOL_TYPE, deviceId);
+ return value != null ? value : protocolType;
}
public void setHbm(boolean hbm) {
@@ -68,8 +84,8 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
}
public boolean isHbm(long deviceId) {
- return Context.getIdentityManager().lookupAttributeBoolean(
- deviceId, getProtocolName() + ".hbm", hbm, false, true);
+ Boolean value = AttributeUtil.lookup(getCacheManager(), Keys.PROTOCOL_HBM, deviceId);
+ return value != null ? value : hbm;
}
public void setIncludeAdc(boolean includeAdc) {
@@ -77,8 +93,9 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
}
public boolean isIncludeAdc(long deviceId) {
- return Context.getIdentityManager().lookupAttributeBoolean(
- deviceId, getProtocolName() + ".includeAdc", includeAdc, false, true);
+ Boolean value = AttributeUtil.lookup(
+ getCacheManager(), Keys.PROTOCOL_INCLUDE_ADC.withPrefix(getProtocolName()), deviceId);
+ return value != null ? value : includeAdc;
}
public void setIncludeRpm(boolean includeRpm) {
@@ -86,8 +103,9 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
}
public boolean isIncludeRpm(long deviceId) {
- return Context.getIdentityManager().lookupAttributeBoolean(
- deviceId, getProtocolName() + ".includeRpm", includeRpm, false, true);
+ Boolean value = AttributeUtil.lookup(
+ getCacheManager(), Keys.PROTOCOL_INCLUDE_RPM.withPrefix(getProtocolName()), deviceId);
+ return value != null ? value : includeRpm;
}
public void setIncludeTemp(boolean includeTemp) {
@@ -95,8 +113,9 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
}
public boolean isIncludeTemp(long deviceId) {
- return Context.getIdentityManager().lookupAttributeBoolean(
- deviceId, getProtocolName() + ".includeTemp", includeTemp, false, true);
+ Boolean value = AttributeUtil.lookup(
+ getCacheManager(), Keys.PROTOCOL_INCLUDE_TEMPERATURE.withPrefix(getProtocolName()), deviceId);
+ return value != null ? value : includeTemp;
}
private Position decode9(
@@ -185,12 +204,16 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
return Position.ALARM_POWER_RESTORED;
case 41:
return Position.ALARM_POWER_CUT;
+ case 42:
+ return Position.ALARM_SOS;
case 46:
return Position.ALARM_ACCELERATION;
case 47:
return Position.ALARM_BRAKING;
case 50:
return Position.ALARM_JAMMING;
+ case 132:
+ return Position.ALARM_DOOR;
default:
return null;
}
@@ -358,7 +381,7 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
}
} else if (attribute.startsWith("GTSL")) {
position.set(Position.KEY_DRIVER_UNIQUE_ID, attribute.split("\\|")[4]);
- } else {
+ } else if (attribute.contains("=")) {
String[] pair = attribute.split("=");
if (pair.length >= 2) {
String value = pair[1].trim();
@@ -381,6 +404,8 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
break;
}
}
+ } else {
+ position.set("serial", attribute.trim());
}
remaining -= attribute.length() + 1;
}
@@ -448,7 +473,7 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
String type = values[index++];
- if (!type.equals("STT") && !type.equals("ALT") && !type.equals("BLE")) {
+ if (!type.equals("STT") && !type.equals("ALT") && !type.equals("BLE") && !type.equals("RES")) {
return null;
}
@@ -461,6 +486,14 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
position.setDeviceId(deviceSession.getDeviceId());
position.set(Position.KEY_TYPE, type);
+ if (type.equals("RES")) {
+ getLastLocation(position, null);
+ position.set(
+ Position.KEY_RESULT,
+ Arrays.stream(values, index, values.length).collect(Collectors.joining(";")));
+ return position;
+ }
+
int mask;
if (type.equals("BLE")) {
mask = 0b1100000110110;
@@ -550,7 +583,9 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
}
if (BitUtil.check(mask, 17)) {
- position.set(Position.KEY_INPUT, Integer.parseInt(values[index++]));
+ int input = Integer.parseInt(values[index++]);
+ position.set(Position.KEY_IGNITION, BitUtil.check(input, 0));
+ position.set(Position.KEY_INPUT, input);
}
if (BitUtil.check(mask, 18)) {
@@ -559,7 +594,8 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
if (type.equals("ALT")) {
if (BitUtil.check(mask, 19)) {
- position.set("alertId", values[index++]);
+ int alertId = Integer.parseInt(values[index++]);
+ position.set(Position.KEY_ALARM, decodeAlert(alertId));
}
if (BitUtil.check(mask, 20)) {
position.set("alertModifier", values[index++]);
@@ -654,19 +690,11 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
}
if (BitUtil.check(mask, 11)) {
- long value = buf.readUnsignedInt();
- if (BitUtil.check(value, 31)) {
- value = -BitUtil.to(value, 31);
- }
- position.setLatitude(value / 1000000.0);
+ position.setLatitude(BufferUtil.readSignedMagnitudeInt(buf) / 1000000.0);
}
if (BitUtil.check(mask, 12)) {
- long value = buf.readUnsignedInt();
- if (BitUtil.check(value, 31)) {
- value = -BitUtil.to(value, 31);
- }
- position.setLongitude(value / 1000000.0);
+ position.setLongitude(BufferUtil.readSignedMagnitudeInt(buf) / 1000000.0);
}
if (BitUtil.check(mask, 13)) {
@@ -732,6 +760,89 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private Collection<Position> decodeCrashReport(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
+
+ if (buf.getByte(buf.readerIndex() + 3) != ';') {
+ return null;
+ }
+
+ String[] values = buf.readCharSequence(23, StandardCharsets.US_ASCII).toString().split(";");
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[1]);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ int currentIndex = Integer.parseInt(values[2]);
+ int totalIndex = Integer.parseInt(values[3]);
+
+ if (crash == null) {
+ crash = Unpooled.buffer();
+ }
+
+ crash.writeBytes(buf.readSlice(buf.readableBytes() - 3));
+
+ if (currentIndex == totalIndex) {
+
+ LinkedList<Position> positions = new LinkedList<>();
+
+ Date crashTime = new DateBuilder()
+ .setDate(crash.readUnsignedByte(), crash.readUnsignedByte(), crash.readUnsignedByte())
+ .setTime(crash.readUnsignedByte(), crash.readUnsignedByte(), crash.readUnsignedByte())
+ .getDate();
+
+ List<Date> times = Arrays.asList(
+ new Date(crashTime.getTime() - 3000),
+ new Date(crashTime.getTime() - 2000),
+ new Date(crashTime.getTime() - 1000),
+ new Date(crashTime.getTime() + 1000));
+
+ for (Date time : times) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setValid(true);
+ position.setTime(time);
+ position.setLatitude(crash.readIntLE() * 0.0000001);
+ position.setLongitude(crash.readIntLE() * 0.0000001);
+ position.setSpeed(UnitsConverter.knotsFromKph(crash.readUnsignedShort() * 0.01));
+ position.setCourse(crash.readUnsignedShort() * 0.01);
+
+ StringBuilder value = new StringBuilder("[");
+ for (int i = 0; i < 100; i++) {
+ if (value.length() > 1) {
+ value.append(",");
+ }
+ value.append("[");
+ value.append(crash.readShortLE());
+ value.append(",");
+ value.append(crash.readShortLE());
+ value.append(",");
+ value.append(crash.readShortLE());
+ value.append("]");
+ }
+ value.append("]");
+
+ position.set(Position.KEY_G_SENSOR, value.toString());
+
+ positions.add(position);
+
+ }
+
+ crash.release();
+ crash = null;
+
+ return positions;
+
+ } else {
+
+ return null;
+
+ }
+
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -740,14 +851,18 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
if (buf.getByte(buf.readerIndex() + 1) == 0) {
+ universal = true;
return decodeBinary(channel, remoteAddress, buf);
} else {
- String[] values = buf.toString(StandardCharsets.US_ASCII).split(";");
+ String[] values = buf.toString(StandardCharsets.US_ASCII).split(";", -1);
prefix = values[0];
- if (prefix.length() < 5) {
+ if (prefix.equals("CRR")) {
+ return decodeCrashReport(channel, remoteAddress, buf);
+ } else if (prefix.length() < 5) {
+ universal = true;
return decodeUniversal(channel, remoteAddress, values);
} else if (prefix.endsWith("HTE")) {
return decodeTravelReport(channel, remoteAddress, values);
diff --git a/src/main/java/org/traccar/protocol/SuntechProtocolEncoder.java b/src/main/java/org/traccar/protocol/SuntechProtocolEncoder.java
index 3b4995110..a4faacf13 100644
--- a/src/main/java/org/traccar/protocol/SuntechProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/SuntechProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,49 +27,95 @@ public class SuntechProtocolEncoder extends StringProtocolEncoder {
super(protocol);
}
- private String getPrefix(Channel channel) {
- String prefix = "SA200CMD";
+ @Override
+ protected Object encodeCommand(Channel channel, Command command) {
+
+ boolean universal = false;
+ String prefix = "SA200";
if (channel != null) {
SuntechProtocolDecoder protocolDecoder =
BasePipelineFactory.getHandler(channel.pipeline(), SuntechProtocolDecoder.class);
if (protocolDecoder != null) {
+ universal = protocolDecoder.getUniversal();
String decoderPrefix = protocolDecoder.getPrefix();
if (decoderPrefix != null && decoderPrefix.length() > 5) {
- prefix = decoderPrefix.substring(0, decoderPrefix.length() - 3) + "CMD";
+ prefix = decoderPrefix.substring(0, decoderPrefix.length() - 3);
}
}
}
- return prefix;
- }
-
- @Override
- protected Object encodeCommand(Channel channel, Command command) {
- String prefix = getPrefix(channel);
+ if (universal) {
+ return encodeUniversalCommand(command);
+ } else {
+ return encodeLegacyCommand(prefix, command);
+ }
+ }
+ protected Object encodeUniversalCommand(Command command) {
switch (command.getType()) {
case Command.TYPE_REBOOT_DEVICE:
- return formatCommand(command, prefix + ";%s;02;Reboot\r", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "CMD;%s;03;03\r", Command.KEY_UNIQUE_ID);
case Command.TYPE_POSITION_SINGLE:
- return formatCommand(command, prefix + ";%s;02;\r", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "CMD;%s;03;01\r", Command.KEY_UNIQUE_ID);
case Command.TYPE_OUTPUT_CONTROL:
- if (command.getAttributes().containsKey(Command.KEY_DATA)) {
- if (command.getAttributes().get(Command.KEY_DATA).equals("1")) {
- return formatCommand(command, prefix + ";%s;02;Enable%s\r",
- Command.KEY_UNIQUE_ID, Command.KEY_INDEX);
- } else {
- return formatCommand(command, prefix + ";%s;02;Disable%s\r",
- Command.KEY_UNIQUE_ID, Command.KEY_INDEX);
+ if (command.getAttributes().get(Command.KEY_DATA).equals("1")) {
+ switch (command.getInteger(Command.KEY_INDEX)) {
+ case 1:
+ return formatCommand(command, "CMD;%s;04;01\r", Command.KEY_UNIQUE_ID);
+ case 2:
+ return formatCommand(command, "CMD;%s;04;03\r", Command.KEY_UNIQUE_ID);
+ case 3:
+ return formatCommand(command, "CMD;%s;04;09\r", Command.KEY_UNIQUE_ID);
+ default:
+ return null;
}
+ } else {
+ switch (command.getInteger(Command.KEY_INDEX)) {
+ case 1:
+ return formatCommand(command, "CMD;%s;04;02\r", Command.KEY_UNIQUE_ID);
+ case 2:
+ return formatCommand(command, "CMD;%s;04;04\r", Command.KEY_UNIQUE_ID);
+ case 3:
+ return formatCommand(command, "CMD;%s;04;10\r", Command.KEY_UNIQUE_ID);
+ default:
+ return null;
+ }
+ }
+ case Command.TYPE_ENGINE_STOP:
+ return formatCommand(command, "CMD;%s;04;01\r", Command.KEY_UNIQUE_ID);
+ case Command.TYPE_ENGINE_RESUME:
+ return formatCommand(command, "CMD;%s;04;02\r", Command.KEY_UNIQUE_ID);
+ case Command.TYPE_ALARM_ARM:
+ return formatCommand(command, "CMD;%s;04;03\r", Command.KEY_UNIQUE_ID);
+ case Command.TYPE_ALARM_DISARM:
+ return formatCommand(command, "CMD;%s;04;04\r", Command.KEY_UNIQUE_ID);
+ default:
+ return null;
+ }
+ }
+
+ protected Object encodeLegacyCommand(String prefix, Command command) {
+ switch (command.getType()) {
+ case Command.TYPE_REBOOT_DEVICE:
+ return formatCommand(command, prefix + "CMD;%s;02;Reboot\r", Command.KEY_UNIQUE_ID);
+ case Command.TYPE_POSITION_SINGLE:
+ return formatCommand(command, prefix + "CMD;%s;02;StatusReq\r", Command.KEY_UNIQUE_ID);
+ case Command.TYPE_OUTPUT_CONTROL:
+ if (command.getAttributes().get(Command.KEY_DATA).equals("1")) {
+ return formatCommand(command, prefix + "CMD;%s;02;Enable%s\r",
+ Command.KEY_UNIQUE_ID, Command.KEY_INDEX);
+ } else {
+ return formatCommand(command, prefix + "CMD;%s;02;Disable%s\r",
+ Command.KEY_UNIQUE_ID, Command.KEY_INDEX);
}
case Command.TYPE_ENGINE_STOP:
- return formatCommand(command, prefix + ";%s;02;Enable1\r", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, prefix + "CMD;%s;02;Enable1\r", Command.KEY_UNIQUE_ID);
case Command.TYPE_ENGINE_RESUME:
- return formatCommand(command, prefix + ";%s;02;Disable1\r", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, prefix + "CMD;%s;02;Disable1\r", Command.KEY_UNIQUE_ID);
case Command.TYPE_ALARM_ARM:
- return formatCommand(command, prefix + ";%s;02;Enable2\r", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, prefix + "CMD;%s;02;Enable2\r", Command.KEY_UNIQUE_ID);
case Command.TYPE_ALARM_DISARM:
- return formatCommand(command, prefix + ";%s;02;Disable2\r", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, prefix + "CMD;%s;02;Disable2\r", Command.KEY_UNIQUE_ID);
default:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/SupermateProtocol.java b/src/main/java/org/traccar/protocol/SupermateProtocol.java
index 46625ddc7..4290b7126 100644
--- a/src/main/java/org/traccar/protocol/SupermateProtocol.java
+++ b/src/main/java/org/traccar/protocol/SupermateProtocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class SupermateProtocol extends BaseProtocol {
- public SupermateProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public SupermateProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "#"));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/SupermateProtocolDecoder.java b/src/main/java/org/traccar/protocol/SupermateProtocolDecoder.java
index 40a25bb91..f53f0f598 100644
--- a/src/main/java/org/traccar/protocol/SupermateProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SupermateProtocolDecoder.java
@@ -18,7 +18,7 @@ package org.traccar.protocol;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
diff --git a/src/main/java/org/traccar/protocol/SviasProtocol.java b/src/main/java/org/traccar/protocol/SviasProtocol.java
index accfa173f..7c6624f7c 100644
--- a/src/main/java/org/traccar/protocol/SviasProtocol.java
+++ b/src/main/java/org/traccar/protocol/SviasProtocol.java
@@ -21,12 +21,15 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
-
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class SviasProtocol extends BaseProtocol {
- public SviasProtocol() {
+ @Inject
+ public SviasProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_CUSTOM,
Command.TYPE_POSITION_SINGLE,
@@ -36,9 +39,9 @@ public class SviasProtocol extends BaseProtocol {
Command.TYPE_ALARM_ARM,
Command.TYPE_ALARM_DISARM,
Command.TYPE_ALARM_REMOVE);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "]"));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/SviasProtocolDecoder.java b/src/main/java/org/traccar/protocol/SviasProtocolDecoder.java
index 7e783f6cd..d7b126167 100644
--- a/src/main/java/org/traccar/protocol/SviasProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SviasProtocolDecoder.java
@@ -24,7 +24,7 @@ import org.traccar.helper.PatternBuilder;
import java.net.SocketAddress;
import java.util.regex.Pattern;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.helper.Parser;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
diff --git a/src/main/java/org/traccar/protocol/SwiftechProtocol.java b/src/main/java/org/traccar/protocol/SwiftechProtocol.java
index 5e2597b93..68cf40d84 100644
--- a/src/main/java/org/traccar/protocol/SwiftechProtocol.java
+++ b/src/main/java/org/traccar/protocol/SwiftechProtocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class SwiftechProtocol extends BaseProtocol {
- public SwiftechProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public SwiftechProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '#'));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/SwiftechProtocolDecoder.java b/src/main/java/org/traccar/protocol/SwiftechProtocolDecoder.java
index 8d0b31c8f..b1cff8b64 100644
--- a/src/main/java/org/traccar/protocol/SwiftechProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SwiftechProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/T55Protocol.java b/src/main/java/org/traccar/protocol/T55Protocol.java
index f5ec19094..cedac275f 100644
--- a/src/main/java/org/traccar/protocol/T55Protocol.java
+++ b/src/main/java/org/traccar/protocol/T55Protocol.java
@@ -21,22 +21,26 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class T55Protocol extends BaseProtocol {
- public T55Protocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public T55Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new T55ProtocolDecoder(T55Protocol.this));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new T55ProtocolDecoder(T55Protocol.this));
diff --git a/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java b/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java
index 230d29216..3be161fb8 100644
--- a/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,9 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
+import org.traccar.config.Keys;
+import org.traccar.helper.model.AttributeUtil;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
@@ -27,6 +28,7 @@ import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
+import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.channels.DatagramChannel;
import java.util.Date;
@@ -124,15 +126,41 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
.expression("([01])") // ignition
.compile();
+ private static final Pattern PATTERN_PUBX = new PatternBuilder()
+ .text("$PUBX,")
+ .number("(d+),") // index
+ .number("(dd)(dd)(dd).d+,") // time (hhmmss)
+ .number("(dd)(dd.d+),([NS]),") // latitude
+ .number("(ddd)(dd.d+),([EW]),") // longitude
+ .number("(-?d+.d+),") // altitude
+ .expression("(..),") // status
+ .number("(d+.d+),") // horizontal accuracy
+ .number("d+.d+,") // vertical accuracy
+ .number("(d+.d+),") // speed
+ .number("(d+.d+),") // course
+ .number("-?d+.d+,") // vertical velocity
+ .expression("[^,]*,") // corrections age
+ .number("(d+.d+),") // hdop
+ .number("(d+.d+),") // vdop
+ .number("d+.d+,") // tdop
+ .number("(d+),") // satellites
+ .number("(d+),") // device id
+ .number("d+")
+ .text("*")
+ .number("xx") // checksum
+ .compile();
+
private Position position = null;
private Position decodeGprmc(
DeviceSession deviceSession, String sentence, SocketAddress remoteAddress, Channel channel) {
- if (deviceSession != null && channel != null && !(channel instanceof DatagramChannel)
- && Context.getIdentityManager().lookupAttributeBoolean(
- deviceSession.getDeviceId(), getProtocolName() + ".ack", false, false, true)) {
- channel.writeAndFlush(new NetworkMessage("OK1\r\n", remoteAddress));
+ if (deviceSession != null && channel != null && !(channel instanceof DatagramChannel)) {
+ boolean ack = AttributeUtil.lookup(
+ getCacheManager(), Keys.PROTOCOL_ACK.withPrefix(getProtocolName()), deviceSession.getDeviceId());
+ if (ack) {
+ channel.writeAndFlush(new NetworkMessage("OK1\r\n", remoteAddress));
+ }
}
Parser parser = new Parser(PATTERN_GPRMC, sentence);
@@ -300,11 +328,44 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private Position decodePubx(Channel channel, SocketAddress remoteAddress, String sentence) {
+
+ Parser parser = new Parser(PATTERN_PUBX, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+
+ position.set(Position.KEY_INDEX, parser.nextInt());
+
+ position.setTime(parser.nextDateTime(Parser.DateTimeFormat.HMS));
+ position.setLatitude(parser.nextCoordinate());
+ position.setLongitude(parser.nextCoordinate());
+ position.setAltitude(parser.nextDouble());
+ position.setValid(!parser.next().equals("NF"));
+ position.setAccuracy(parser.nextDouble());
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
+ position.setCourse(parser.nextDouble());
+
+ position.set(Position.KEY_HDOP, parser.nextDouble());
+ position.set(Position.KEY_VDOP, parser.nextDouble());
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession != null) {
+ position.setDeviceId(deviceSession.getDeviceId());
+ return position;
+ }
+
+ return null;
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- String sentence = (String) msg;
+ String sentence = ((String) msg).trim();
DeviceSession deviceSession;
@@ -320,6 +381,10 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
sentence = sentence.substring(index);
} else {
deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null && remoteAddress instanceof InetSocketAddress) {
+ String host = ((InetSocketAddress) remoteAddress).getHostString();
+ deviceSession = getDeviceSession(channel, remoteAddress, host);
+ }
}
if (sentence.startsWith("$PGID")) {
@@ -354,6 +419,8 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
return decodeGpiop(deviceSession, sentence);
} else if (sentence.startsWith("QZE")) {
return decodeQze(channel, remoteAddress, sentence);
+ } else if (sentence.startsWith("$PUBX")) {
+ return decodePubx(channel, remoteAddress, sentence);
}
return null;
diff --git a/src/main/java/org/traccar/protocol/T57Protocol.java b/src/main/java/org/traccar/protocol/T57Protocol.java
index f67f82318..4bafe8c6d 100644
--- a/src/main/java/org/traccar/protocol/T57Protocol.java
+++ b/src/main/java/org/traccar/protocol/T57Protocol.java
@@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class T57Protocol extends BaseProtocol {
- public T57Protocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public T57Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new T57FrameDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/T57ProtocolDecoder.java b/src/main/java/org/traccar/protocol/T57ProtocolDecoder.java
index 2a3cca3e4..d9fd1c8cf 100644
--- a/src/main/java/org/traccar/protocol/T57ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/T57ProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/T800xProtocol.java b/src/main/java/org/traccar/protocol/T800xProtocol.java
index 8b91265cb..253c3cb73 100644
--- a/src/main/java/org/traccar/protocol/T800xProtocol.java
+++ b/src/main/java/org/traccar/protocol/T800xProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,21 +19,32 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class T800xProtocol extends BaseProtocol {
- public T800xProtocol() {
+ @Inject
+ public T800xProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_CUSTOM);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 3, 2, -5, 0));
pipeline.addLast(new T800xProtocolEncoder(T800xProtocol.this));
pipeline.addLast(new T800xProtocolDecoder(T800xProtocol.this));
}
});
+ addServer(new TrackerServer(config, getName(), true) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
+ pipeline.addLast(new T800xProtocolEncoder(T800xProtocol.this));
+ pipeline.addLast(new T800xProtocolDecoder(T800xProtocol.this));
+ }
+ });
}
}
diff --git a/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java b/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java
index 7f7873e50..6e09e6e3b 100644
--- a/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java
@@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BcdUtil;
@@ -58,6 +58,7 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_DRIVER_BEHAVIOR_1 = 0x05; // 0x2626
public static final int MSG_DRIVER_BEHAVIOR_2 = 0x06; // 0x2626
public static final int MSG_BLE = 0x10;
+ public static final int MSG_NETWORK_2 = 0x11;
public static final int MSG_GPS_2 = 0x13;
public static final int MSG_ALARM_2 = 0x14;
public static final int MSG_COMMAND = 0x81;
@@ -77,7 +78,7 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
}
}
- private String decodeAlarm(int value) {
+ private String decodeAlarm1(int value) {
switch (value) {
case 1:
return Position.ALARM_POWER_CUT;
@@ -107,6 +108,28 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
}
}
+ private String decodeAlarm2(int value) {
+ switch (value) {
+ case 1:
+ case 4:
+ return Position.ALARM_REMOVING;
+ case 2:
+ return Position.ALARM_TAMPERING;
+ case 3:
+ return Position.ALARM_SOS;
+ case 5:
+ return Position.ALARM_FALL_DOWN;
+ case 6:
+ return Position.ALARM_LOW_BATTERY;
+ case 14:
+ return Position.ALARM_GEOFENCE_ENTER;
+ case 15:
+ return Position.ALARM_GEOFENCE_EXIT;
+ default:
+ return null;
+ }
+ }
+
private Date readDate(ByteBuf buf) {
return new DateBuilder()
.setYear(BcdUtil.readInteger(buf, 2))
@@ -145,7 +168,7 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
return decodePosition(channel, deviceSession, buf, type, index, imei);
- } else if (type == MSG_NETWORK && header == 0x2727) {
+ } else if (type == MSG_NETWORK && header == 0x2727 || type == MSG_NETWORK_2) {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
@@ -232,6 +255,11 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
return null;
}
+ private double decodeBleTemp(ByteBuf buf) {
+ int value = buf.readUnsignedShort();
+ return (BitUtil.check(value, 15) ? -BitUtil.to(value, 15) : BitUtil.to(value, 15)) * 0.01;
+ }
+
private Position decodeBle(
Channel channel, DeviceSession deviceSession, ByteBuf buf, int type, int index, ByteBuf imei) {
@@ -281,7 +309,7 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
position.set("tag" + i + "Id", ByteBufUtil.hexDump(buf.readSlice(6)));
position.set("tag" + i + "Battery", buf.readUnsignedByte() * 0.01 + 2);
buf.readUnsignedByte(); // battery level
- position.set("tag" + i + "Temp", buf.readUnsignedShort() * 0.01);
+ position.set("tag" + i + "Temp", decodeBleTemp(buf));
position.set("tag" + i + "Humidity", buf.readUnsignedShort() * 0.01);
position.set("tag" + i + "LightSensor", buf.readUnsignedShort());
position.set("tag" + i + "Rssi", buf.readUnsignedByte() - 128);
@@ -290,7 +318,7 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
position.set("tag" + i + "Id", ByteBufUtil.hexDump(buf.readSlice(6)));
position.set("tag" + i + "Battery", buf.readUnsignedByte() * 0.01 + 2);
buf.readUnsignedByte(); // battery level
- position.set("tag" + i + "Temp", buf.readUnsignedShort() * 0.01);
+ position.set("tag" + i + "Temp", decodeBleTemp(buf));
position.set("tag" + i + "Door", buf.readUnsignedByte() > 0);
position.set("tag" + i + "Rssi", buf.readUnsignedByte() - 128);
break;
@@ -369,7 +397,7 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
}
int alarm = buf.readUnsignedByte();
- position.set(Position.KEY_ALARM, decodeAlarm(alarm));
+ position.set(Position.KEY_ALARM, header != 0x2727 ? decodeAlarm1(alarm) : decodeAlarm2(alarm));
if (header != 0x2727) {
@@ -384,7 +412,7 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
if (BitUtil.check(status, 6)) {
- position.setValid(!BitUtil.check(status, 7));
+ position.setValid(true);
position.setTime(readDate(buf));
position.setAltitude(buf.readFloatLE());
position.setLongitude(buf.readFloatLE());
diff --git a/src/main/java/org/traccar/protocol/TaipPrefixEncoder.java b/src/main/java/org/traccar/protocol/TaipPrefixEncoder.java
index 02c111b01..48419af2a 100644
--- a/src/main/java/org/traccar/protocol/TaipPrefixEncoder.java
+++ b/src/main/java/org/traccar/protocol/TaipPrefixEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,13 +15,14 @@
*/
package org.traccar.protocol;
+import com.google.inject.Inject;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
-import org.traccar.Context;
import org.traccar.Protocol;
+import org.traccar.config.Config;
import org.traccar.config.Keys;
import java.util.List;
@@ -30,14 +31,20 @@ import java.util.List;
public class TaipPrefixEncoder extends MessageToMessageEncoder<ByteBuf> {
private final Protocol protocol;
+ private Config config;
public TaipPrefixEncoder(Protocol protocol) {
this.protocol = protocol;
}
+ @Inject
+ public void setConfig(Config config) {
+ this.config = config;
+ }
+
@Override
- protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
- if (Context.getConfig().getBoolean(Keys.PROTOCOL_PREFIX.withPrefix(protocol.getName()))) {
+ protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) {
+ if (config.getBoolean(Keys.PROTOCOL_PREFIX.withPrefix(protocol.getName()))) {
out.add(Unpooled.wrappedBuffer(Unpooled.wrappedBuffer(new byte[] {0x20, 0x20, 0x06, 0x00}), msg.retain()));
} else {
out.add(msg.retain());
diff --git a/src/main/java/org/traccar/protocol/TaipProtocol.java b/src/main/java/org/traccar/protocol/TaipProtocol.java
index 0966cfd7c..943ec98c5 100644
--- a/src/main/java/org/traccar/protocol/TaipProtocol.java
+++ b/src/main/java/org/traccar/protocol/TaipProtocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class TaipProtocol extends BaseProtocol {
- public TaipProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public TaipProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '<'));
pipeline.addLast(new TaipPrefixEncoder(TaipProtocol.this));
pipeline.addLast(new StringDecoder());
@@ -35,9 +39,9 @@ public class TaipProtocol extends BaseProtocol {
pipeline.addLast(new TaipProtocolDecoder(TaipProtocol.this));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new TaipPrefixEncoder(TaipProtocol.this));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/TaipProtocolDecoder.java b/src/main/java/org/traccar/protocol/TaipProtocolDecoder.java
index ec0ce1931..e5e84b7c4 100644
--- a/src/main/java/org/traccar/protocol/TaipProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TaipProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/TechTltProtocol.java b/src/main/java/org/traccar/protocol/TechTltProtocol.java
index 0cffb452d..191dd9ccc 100644
--- a/src/main/java/org/traccar/protocol/TechTltProtocol.java
+++ b/src/main/java/org/traccar/protocol/TechTltProtocol.java
@@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class TechTltProtocol extends BaseProtocol {
- public TechTltProtocol() {
- addServer(new TrackerServer(true, getName()) {
+ @Inject
+ public TechTltProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new TechTltProtocolDecoder(TechTltProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/TechTltProtocolDecoder.java b/src/main/java/org/traccar/protocol/TechTltProtocolDecoder.java
index 17f5c80fa..94efacc63 100644
--- a/src/main/java/org/traccar/protocol/TechTltProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TechTltProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -110,7 +110,7 @@ public class TechTltProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_SATELLITES, parser.nextInt());
- position.setNetwork(new Network(CellTower.fromLacCid(parser.nextInt(), parser.nextInt())));
+ position.setNetwork(new Network(CellTower.fromLacCid(getConfig(), parser.nextInt(), parser.nextInt())));
return position;
}
diff --git a/src/main/java/org/traccar/protocol/TechtoCruzFrameDecoder.java b/src/main/java/org/traccar/protocol/TechtoCruzFrameDecoder.java
new file mode 100644
index 000000000..8b9152e8b
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TechtoCruzFrameDecoder.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2021 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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 TechtoCruzFrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ int lengthStart = buf.readerIndex() + 3;
+ int lengthEnd = buf.indexOf(lengthStart, buf.writerIndex(), (byte) ',');
+ if (lengthEnd > 0) {
+ int length = lengthStart
+ + Integer.parseInt(buf.toString(lengthStart, lengthEnd - lengthStart, StandardCharsets.US_ASCII));
+ if (buf.readableBytes() >= length) {
+ return buf.readRetainedSlice(length);
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/TechtoCruzProtocol.java b/src/main/java/org/traccar/protocol/TechtoCruzProtocol.java
index 890a8abb1..265a3eb64 100644
--- a/src/main/java/org/traccar/protocol/TechtoCruzProtocol.java
+++ b/src/main/java/org/traccar/protocol/TechtoCruzProtocol.java
@@ -15,20 +15,23 @@
*/
package org.traccar.protocol;
-import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class TechtoCruzProtocol extends BaseProtocol {
- public TechtoCruzProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public TechtoCruzProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
- pipeline.addLast(new LineBasedFrameDecoder(1024));
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
+ pipeline.addLast(new TechtoCruzFrameDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new TechtoCruzProtocolDecoder(TechtoCruzProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/TechtoCruzProtocolDecoder.java b/src/main/java/org/traccar/protocol/TechtoCruzProtocolDecoder.java
index 6b9f0edb6..09efcb7d4 100644
--- a/src/main/java/org/traccar/protocol/TechtoCruzProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TechtoCruzProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/TekProtocol.java b/src/main/java/org/traccar/protocol/TekProtocol.java
index c1d78e6f5..54e860d79 100644
--- a/src/main/java/org/traccar/protocol/TekProtocol.java
+++ b/src/main/java/org/traccar/protocol/TekProtocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class TekProtocol extends BaseProtocol {
- public TekProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public TekProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new TekFrameDecoder());
pipeline.addLast(new TekProtocolDecoder(TekProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/TekProtocolDecoder.java b/src/main/java/org/traccar/protocol/TekProtocolDecoder.java
index 33ff51d2d..819c7e819 100644
--- a/src/main/java/org/traccar/protocol/TekProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TekProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
diff --git a/src/main/java/org/traccar/protocol/TelemaxProtocol.java b/src/main/java/org/traccar/protocol/TelemaxProtocol.java
index 838da9df1..9e9cbb50e 100644
--- a/src/main/java/org/traccar/protocol/TelemaxProtocol.java
+++ b/src/main/java/org/traccar/protocol/TelemaxProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class TelemaxProtocol extends BaseProtocol {
- public TelemaxProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public TelemaxProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/TelemaxProtocolDecoder.java b/src/main/java/org/traccar/protocol/TelemaxProtocolDecoder.java
index 9369ab101..f6f6f5379 100644
--- a/src/main/java/org/traccar/protocol/TelemaxProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TelemaxProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.model.Position;
diff --git a/src/main/java/org/traccar/protocol/TelicProtocol.java b/src/main/java/org/traccar/protocol/TelicProtocol.java
index 991befa19..9ef7864ca 100644
--- a/src/main/java/org/traccar/protocol/TelicProtocol.java
+++ b/src/main/java/org/traccar/protocol/TelicProtocol.java
@@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class TelicProtocol extends BaseProtocol {
- public TelicProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public TelicProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new TelicFrameDecoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/TelicProtocolDecoder.java b/src/main/java/org/traccar/protocol/TelicProtocolDecoder.java
index a4f9e2989..9681dc565 100644
--- a/src/main/java/org/traccar/protocol/TelicProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TelicProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/TeltonikaFrameDecoder.java b/src/main/java/org/traccar/protocol/TeltonikaFrameDecoder.java
index c30fee6e3..3a0962584 100644
--- a/src/main/java/org/traccar/protocol/TeltonikaFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/TeltonikaFrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,7 +30,7 @@ public class TeltonikaFrameDecoder extends BaseFrameDecoder {
ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
while (buf.isReadable() && buf.getByte(buf.readerIndex()) == (byte) 0xff) {
- buf.skipBytes(1);
+ return buf.readRetainedSlice(1);
}
if (buf.readableBytes() < MESSAGE_MINIMUM_LENGTH) {
diff --git a/src/main/java/org/traccar/protocol/TeltonikaProtocol.java b/src/main/java/org/traccar/protocol/TeltonikaProtocol.java
index 5817b86be..38283cb64 100644
--- a/src/main/java/org/traccar/protocol/TeltonikaProtocol.java
+++ b/src/main/java/org/traccar/protocol/TeltonikaProtocol.java
@@ -18,24 +18,28 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class TeltonikaProtocol extends BaseProtocol {
- public TeltonikaProtocol() {
+ @Inject
+ public TeltonikaProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_CUSTOM);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new TeltonikaFrameDecoder());
pipeline.addLast(new TeltonikaProtocolEncoder(TeltonikaProtocol.this));
pipeline.addLast(new TeltonikaProtocolDecoder(TeltonikaProtocol.this, false));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new TeltonikaProtocolEncoder(TeltonikaProtocol.this));
pipeline.addLast(new TeltonikaProtocolDecoder(TeltonikaProtocol.this, true));
}
diff --git a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
index 89ae48b3a..929eca8aa 100644
--- a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,8 +20,8 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
+import org.traccar.model.Device;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.config.Keys;
@@ -39,11 +39,15 @@ import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.function.BiConsumer;
public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
private static final int IMAGE_PACKET_MAX = 2048;
+ private static final Map<Integer, Map<Set<String>, BiConsumer<Position, ByteBuf>>> PARAMETERS = new HashMap<>();
+
private final boolean connectionless;
private boolean extended;
private final Map<Long, ByteBuf> photos = new HashMap<>();
@@ -55,7 +59,11 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
public TeltonikaProtocolDecoder(Protocol protocol, boolean connectionless) {
super(protocol);
this.connectionless = connectionless;
- this.extended = Context.getConfig().getBoolean(Keys.PROTOCOL_EXTENDED.withPrefix(getProtocolName()));
+ }
+
+ @Override
+ protected void init() {
+ this.extended = getConfig().getBoolean(Keys.PROTOCOL_EXTENDED.withPrefix(getProtocolName()));
}
private void parseIdentification(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
@@ -116,7 +124,8 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
return printable;
}
- private void decodeSerial(Channel channel, SocketAddress remoteAddress, Position position, ByteBuf buf) {
+ private void decodeSerial(
+ Channel channel, SocketAddress remoteAddress, DeviceSession deviceSession, Position position, ByteBuf buf) {
getLastLocation(position, null);
@@ -145,10 +154,9 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
channel, remoteAddress, photoId,
photo.writerIndex(), Math.min(IMAGE_PACKET_MAX, photo.writableBytes()));
} else {
- String uniqueId = Context.getIdentityManager().getById(position.getDeviceId()).getUniqueId();
photos.remove(photoId);
try {
- position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(uniqueId, photo, "jpg"));
+ position.set(Position.KEY_IMAGE, writeMediaFile(deviceSession.getUniqueId(), photo, "jpg"));
} finally {
photo.release();
}
@@ -181,191 +189,193 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
}
- private long readValue(ByteBuf buf, int length, boolean signed) {
+ private long readValue(ByteBuf buf, int length) {
switch (length) {
case 1:
- return signed ? buf.readByte() : buf.readUnsignedByte();
+ return buf.readUnsignedByte();
case 2:
- return signed ? buf.readShort() : buf.readUnsignedShort();
+ return buf.readUnsignedShort();
case 4:
- return signed ? buf.readInt() : buf.readUnsignedInt();
+ return buf.readUnsignedInt();
default:
return buf.readLong();
}
}
- private void decodeOtherParameter(Position position, int id, ByteBuf buf, int length) {
- switch (id) {
- case 1:
- case 2:
- case 3:
- case 4:
- position.set("di" + id, readValue(buf, length, false));
- break;
- case 9:
- position.set(Position.PREFIX_ADC + 1, readValue(buf, length, false));
- break;
- case 10:
- position.set(Position.PREFIX_ADC + 2, readValue(buf, length, false));
- break;
- case 16:
- position.set(Position.KEY_ODOMETER, readValue(buf, length, false));
- break;
- case 17:
- position.set("axisX", readValue(buf, length, true));
- break;
- case 18:
- position.set("axisY", readValue(buf, length, true));
- break;
- case 19:
- position.set("axisZ", readValue(buf, length, true));
- break;
- case 21:
- position.set(Position.KEY_RSSI, readValue(buf, length, false));
- break;
- case 25:
- case 26:
- case 27:
- case 28:
- position.set(Position.PREFIX_TEMP + (id - 24 + 4), readValue(buf, length, true) * 0.1);
- break;
- case 66:
- position.set(Position.KEY_POWER, readValue(buf, length, false) * 0.001);
- break;
- case 67:
- position.set(Position.KEY_BATTERY, readValue(buf, length, false) * 0.001);
- break;
- case 72:
- case 73:
- case 74:
- position.set(Position.PREFIX_TEMP + (id - 71), readValue(buf, length, true) * 0.1);
- break;
- case 78:
- long driverUniqueId = readValue(buf, length, false);
- if (driverUniqueId != 0) {
- position.set(Position.KEY_DRIVER_UNIQUE_ID, String.format("%016X", driverUniqueId));
- }
- break;
- case 80:
- position.set("workMode", readValue(buf, length, false));
- break;
- case 90:
- position.set(Position.KEY_DOOR, readValue(buf, length, false));
- break;
- case 115:
- position.set(Position.KEY_COOLANT_TEMP, readValue(buf, length, true) * 0.1);
- break;
- case 179:
- position.set(Position.PREFIX_OUT + 1, readValue(buf, length, false) == 1);
- break;
- case 180:
- position.set(Position.PREFIX_OUT + 2, readValue(buf, length, false) == 1);
- break;
- case 181:
- position.set(Position.KEY_PDOP, readValue(buf, length, false) * 0.1);
- break;
- case 182:
- position.set(Position.KEY_HDOP, readValue(buf, length, false) * 0.1);
- break;
- case 199:
- position.set(Position.KEY_ODOMETER_TRIP, readValue(buf, length, false));
- break;
- case 236:
- if (readValue(buf, length, false) == 1) {
- position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
- }
- break;
- case 239:
- position.set(Position.KEY_IGNITION, readValue(buf, length, false) == 1);
- break;
- case 240:
- position.set(Position.KEY_MOTION, readValue(buf, length, false) == 1);
- break;
- case 241:
- position.set(Position.KEY_OPERATOR, readValue(buf, length, false));
- break;
- case 253:
- switch ((int) readValue(buf, length, false)) {
- case 1:
- position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
- break;
- case 2:
- position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
- break;
- case 3:
- position.set(Position.KEY_ALARM, Position.ALARM_CORNERING);
- break;
- default:
- break;
- }
- break;
- default:
- position.set(Position.PREFIX_IO + id, readValue(buf, length, false));
- break;
- }
+ private static void register(int id, Set<String> models, BiConsumer<Position, ByteBuf> handler) {
+ PARAMETERS.computeIfAbsent(id, key -> new HashMap<>()).put(models, handler);
+ }
+
+ static {
+ var fmbXXX = Set.of(
+ "FMB001", "FMB010", "FMB002", "FMB020", "FMB003", "FMB110", "FMB120", "FMB122", "FMB125", "FMB130",
+ "FMB140", "FMU125", "FMB900", "FMB920", "FMB962", "FMB964", "FM3001", "FMB202", "FMB204", "FMB206",
+ "FMT100", "MTB100", "FMP100", "MSP500");
+
+ register(1, null, (p, b) -> p.set(Position.PREFIX_IN + 1, b.readUnsignedByte() > 0));
+ register(2, null, (p, b) -> p.set(Position.PREFIX_IN + 2, b.readUnsignedByte() > 0));
+ register(3, null, (p, b) -> p.set(Position.PREFIX_IN + 3, b.readUnsignedByte() > 0));
+ register(4, null, (p, b) -> p.set(Position.PREFIX_IN + 4, b.readUnsignedByte() > 0));
+ register(9, fmbXXX, (p, b) -> p.set(Position.PREFIX_ADC + 1, b.readUnsignedShort() * 0.001));
+ register(10, fmbXXX, (p, b) -> p.set(Position.PREFIX_ADC + 2, b.readUnsignedShort() * 0.001));
+ register(11, fmbXXX, (p, b) -> p.set(Position.KEY_ICCID, String.valueOf(b.readLong())));
+ register(12, fmbXXX, (p, b) -> p.set(Position.KEY_FUEL_USED, b.readUnsignedInt() * 0.001));
+ register(13, fmbXXX, (p, b) -> p.set(Position.KEY_FUEL_CONSUMPTION, b.readUnsignedShort() * 0.01));
+ register(16, null, (p, b) -> p.set(Position.KEY_ODOMETER, b.readUnsignedInt()));
+ register(17, null, (p, b) -> p.set("axisX", b.readShort()));
+ register(18, null, (p, b) -> p.set("axisY", b.readShort()));
+ register(19, null, (p, b) -> p.set("axisZ", b.readShort()));
+ register(21, null, (p, b) -> p.set(Position.KEY_RSSI, b.readUnsignedByte()));
+ register(24, fmbXXX, (p, b) -> p.setSpeed(UnitsConverter.knotsFromKph(b.readUnsignedShort())));
+ register(25, null, (p, b) -> p.set("bleTemp1", b.readShort() * 0.01));
+ register(26, null, (p, b) -> p.set("bleTemp2", b.readShort() * 0.01));
+ register(27, null, (p, b) -> p.set("bleTemp3", b.readShort() * 0.01));
+ register(28, null, (p, b) -> p.set("bleTemp4", b.readShort() * 0.01));
+ register(66, null, (p, b) -> p.set(Position.KEY_POWER, b.readUnsignedShort() * 0.001));
+ register(67, null, (p, b) -> p.set(Position.KEY_BATTERY, b.readUnsignedShort() * 0.001));
+ register(68, fmbXXX, (p, b) -> p.set("batteryCurrent", b.readUnsignedShort() * 0.001));
+ register(72, fmbXXX, (p, b) -> p.set(Position.PREFIX_TEMP + 1, b.readInt() * 0.1));
+ register(73, fmbXXX, (p, b) -> p.set(Position.PREFIX_TEMP + 2, b.readInt() * 0.1));
+ register(74, fmbXXX, (p, b) -> p.set(Position.PREFIX_TEMP + 3, b.readInt() * 0.1));
+ register(75, fmbXXX, (p, b) -> p.set(Position.PREFIX_TEMP + 4, b.readInt() * 0.1));
+ register(78, null, (p, b) -> {
+ long driverUniqueId = b.readLong();
+ if (driverUniqueId > 0) {
+ p.set(Position.KEY_DRIVER_UNIQUE_ID, String.format("%016X", driverUniqueId));
+ }
+ });
+ register(80, fmbXXX, (p, b) -> p.set("dataMode", b.readUnsignedByte()));
+ register(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(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(206, fmbXXX, (p, b) -> p.set("lac", b.readUnsignedShort()));
+ 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(253, null, (p, b) -> {
+ switch (b.readUnsignedByte()) {
+ case 1:
+ p.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
+ break;
+ case 2:
+ p.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
+ break;
+ case 3:
+ p.set(Position.KEY_ALARM, Position.ALARM_CORNERING);
+ break;
+ default:
+ break;
+ }
+ });
}
private void decodeGh3000Parameter(Position position, int id, ByteBuf buf, int length) {
switch (id) {
case 1:
- position.set(Position.KEY_BATTERY_LEVEL, readValue(buf, length, false));
+ position.set(Position.KEY_BATTERY_LEVEL, readValue(buf, length));
break;
case 2:
- position.set("usbConnected", readValue(buf, length, false) == 1);
+ position.set("usbConnected", readValue(buf, length) == 1);
break;
case 5:
- position.set("uptime", readValue(buf, length, false));
+ position.set("uptime", readValue(buf, length));
break;
case 20:
- position.set(Position.KEY_HDOP, readValue(buf, length, false) * 0.1);
+ position.set(Position.KEY_HDOP, readValue(buf, length) * 0.1);
break;
case 21:
- position.set(Position.KEY_VDOP, readValue(buf, length, false) * 0.1);
+ position.set(Position.KEY_VDOP, readValue(buf, length) * 0.1);
break;
case 22:
- position.set(Position.KEY_PDOP, readValue(buf, length, false) * 0.1);
+ position.set(Position.KEY_PDOP, readValue(buf, length) * 0.1);
break;
case 67:
- position.set(Position.KEY_BATTERY, readValue(buf, length, false) * 0.001);
+ position.set(Position.KEY_BATTERY, readValue(buf, length) * 0.001);
break;
case 221:
- position.set("button", readValue(buf, length, false));
+ position.set("button", readValue(buf, length));
break;
case 222:
- if (readValue(buf, length, false) == 1) {
+ if (readValue(buf, length) == 1) {
position.set(Position.KEY_ALARM, Position.ALARM_SOS);
}
break;
case 240:
- position.set(Position.KEY_MOTION, readValue(buf, length, false) == 1);
+ position.set(Position.KEY_MOTION, readValue(buf, length) == 1);
break;
case 244:
- position.set(Position.KEY_ROAMING, readValue(buf, length, false) == 1);
+ position.set(Position.KEY_ROAMING, readValue(buf, length) == 1);
break;
default:
- position.set(Position.PREFIX_IO + id, readValue(buf, length, false));
+ position.set(Position.PREFIX_IO + id, readValue(buf, length));
break;
}
}
- private void decodeParameter(Position position, int id, ByteBuf buf, int length, int codec) {
+ private void decodeParameter(Position position, int id, ByteBuf buf, int length, int codec, String model) {
if (codec == CODEC_GH3000) {
decodeGh3000Parameter(position, id, buf, length);
} else {
- decodeOtherParameter(position, id, buf, length);
+ int index = buf.readerIndex();
+ boolean decoded = false;
+ for (var entry : PARAMETERS.getOrDefault(id, new HashMap<>()).entrySet()) {
+ if (entry.getKey() == null || model != null && entry.getKey().contains(model)) {
+ entry.getValue().accept(position, buf);
+ decoded = true;
+ break;
+ }
+ }
+ if (decoded) {
+ buf.readerIndex(index + length);
+ } else {
+ position.set(Position.PREFIX_IO + id, readValue(buf, length));
+ }
+ }
+ }
+
+ private void decodeCell(
+ Position position, Network network, String mncKey, String lacKey, String cidKey, String rssiKey) {
+ if (position.hasAttribute(mncKey) && position.hasAttribute(lacKey) && position.hasAttribute(cidKey)) {
+ CellTower cellTower = CellTower.from(
+ getConfig().getInteger(Keys.GEOLOCATION_MCC),
+ ((Number) position.getAttributes().remove(mncKey)).intValue(),
+ ((Number) position.getAttributes().remove(lacKey)).intValue(),
+ ((Number) position.getAttributes().remove(cidKey)).longValue());
+ cellTower.setSignalStrength(((Number) position.getAttributes().remove(rssiKey)).intValue());
+ network.addCellTower(cellTower);
}
}
- private void decodeNetwork(Position position) {
- long cid = position.getLong(Position.PREFIX_IO + 205);
- int lac = position.getInteger(Position.PREFIX_IO + 206);
- if (cid != 0 && lac != 0) {
- CellTower cellTower = CellTower.fromLacCid(lac, cid);
- long operator = position.getInteger(Position.KEY_OPERATOR);
- if (operator != 0) {
- cellTower.setOperator(operator);
+ private void decodeNetwork(Position position, String model) {
+ if ("TAT100".equals(model)) {
+ Network network = new Network();
+ decodeCell(position, network, "io1200", "io287", "io288", "io289");
+ decodeCell(position, network, "io1201", "io290", "io291", "io292");
+ decodeCell(position, network, "io1202", "io293", "io294", "io295");
+ decodeCell(position, network, "io1203", "io296", "io297", "io298");
+ if (network.getCellTowers() != null) {
+ position.setNetwork(network);
+ }
+ } else {
+ Integer cid = (Integer) position.getAttributes().remove("cid");
+ Integer lac = (Integer) position.getAttributes().remove("lac");
+ if (cid != null && lac != null) {
+ CellTower cellTower = CellTower.fromLacCid(getConfig(), lac, cid);
+ long operator = position.getInteger(Position.KEY_OPERATOR);
+ if (operator >= 1000) {
+ cellTower.setOperator(operator);
+ }
+ position.setNetwork(new Network(cellTower));
}
- position.setNetwork(new Network(cellTower));
}
}
@@ -384,7 +394,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
}
- private void decodeLocation(Position position, ByteBuf buf, int codec) {
+ private void decodeLocation(Position position, ByteBuf buf, int codec, String model) {
int globalMask = 0x0f;
@@ -422,7 +432,8 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
if (BitUtil.check(locationMask, 5)) {
- CellTower cellTower = CellTower.fromLacCid(buf.readUnsignedShort(), buf.readUnsignedShort());
+ CellTower cellTower = CellTower.fromLacCid(
+ getConfig(), buf.readUnsignedShort(), buf.readUnsignedShort());
if (BitUtil.check(locationMask, 6)) {
cellTower.setSignalStrength((int) buf.readUnsignedByte());
@@ -480,7 +491,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
if (BitUtil.check(globalMask, 1)) {
int cnt = readExtByte(buf, codec, CODEC_8_EXT);
for (int j = 0; j < cnt; j++) {
- decodeParameter(position, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16), buf, 1, codec);
+ decodeParameter(position, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16), buf, 1, codec, model);
}
}
@@ -488,7 +499,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
if (BitUtil.check(globalMask, 2)) {
int cnt = readExtByte(buf, codec, CODEC_8_EXT);
for (int j = 0; j < cnt; j++) {
- decodeParameter(position, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16), buf, 2, codec);
+ decodeParameter(position, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16), buf, 2, codec, model);
}
}
@@ -496,7 +507,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
if (BitUtil.check(globalMask, 3)) {
int cnt = readExtByte(buf, codec, CODEC_8_EXT);
for (int j = 0; j < cnt; j++) {
- decodeParameter(position, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16), buf, 4, codec);
+ decodeParameter(position, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16), buf, 4, codec, model);
}
}
@@ -504,7 +515,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
if (codec == CODEC_8 || codec == CODEC_8_EXT || codec == CODEC_16) {
int cnt = readExtByte(buf, codec, CODEC_8_EXT);
for (int j = 0; j < cnt; j++) {
- decodeOtherParameter(position, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16), buf, 8);
+ decodeParameter(position, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16), buf, 8, codec, model);
}
}
@@ -558,7 +569,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
}
- decodeNetwork(position);
+ decodeNetwork(position, model);
}
@@ -574,10 +585,10 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
int count = buf.readUnsignedByte();
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
-
if (deviceSession == null) {
return null;
}
+ String model = getCacheManager().getObject(Device.class, deviceSession.getDeviceId()).getModel();
for (int i = 0; i < count; i++) {
Position position = new Position(getProtocolName());
@@ -597,9 +608,9 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
ByteBufUtil.hexDump(buf.readSlice(length)));
}
} else if (codec == CODEC_12) {
- decodeSerial(channel, remoteAddress, position, buf);
+ decodeSerial(channel, remoteAddress, deviceSession, position, buf);
} else {
- decodeLocation(position, buf, codec);
+ decodeLocation(position, buf, codec, model);
}
if (!position.getOutdated() || !position.getAttributes().isEmpty()) {
@@ -636,9 +647,11 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
}
- private Object decodeTcp(Channel channel, SocketAddress remoteAddress, ByteBuf buf) throws Exception {
+ private Object decodeTcp(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
- if (buf.getUnsignedShort(0) > 0) {
+ if (buf.readableBytes() == 1 && buf.readUnsignedByte() == 0xff) {
+ return null;
+ } else if (buf.getUnsignedShort(0) > 0) {
parseIdentification(channel, remoteAddress, buf);
} else {
buf.skipBytes(4);
@@ -648,7 +661,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- private Object decodeUdp(Channel channel, SocketAddress remoteAddress, ByteBuf buf) throws Exception {
+ private Object decodeUdp(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
buf.readUnsignedShort(); // length
buf.readUnsignedShort(); // packet id
diff --git a/src/main/java/org/traccar/protocol/TeraTrackProtocol.java b/src/main/java/org/traccar/protocol/TeraTrackProtocol.java
new file mode 100644
index 000000000..73219cc5e
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TeraTrackProtocol.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
+
+public class TeraTrackProtocol extends BaseProtocol {
+
+ @Inject
+ public TeraTrackProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
+ pipeline.addLast(new JsonFrameDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new TeraTrackProtocolDecoder(TeraTrackProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/TeraTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/TeraTrackProtocolDecoder.java
new file mode 100644
index 000000000..313210f63
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TeraTrackProtocolDecoder.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.session.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import javax.json.Json;
+import javax.json.JsonObject;
+import java.io.StringReader;
+import java.net.SocketAddress;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.TimeZone;
+
+public class TeraTrackProtocolDecoder extends BaseProtocolDecoder {
+
+ public TeraTrackProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ JsonObject json = Json.createReader(new StringReader((String) msg)).readObject();
+
+ String deviceId = json.getString("MDeviceID");
+ String imei = json.getString("IMEI");
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, deviceId, imei);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ position.setTime(dateFormat.parse(json.getString("DateTime")));
+
+ position.setValid(true);
+ position.setLatitude(Double.parseDouble(json.getString("Latitude")));
+ position.setLongitude(Double.parseDouble(json.getString("Longitude")));
+ 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_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")));
+ position.set(Position.KEY_RSSI, Integer.parseInt(json.getString("GSM")));
+
+ if (channel != null && json.getString("MessageAck").equals("1")) {
+ channel.writeAndFlush(new NetworkMessage("{01}", remoteAddress));
+ }
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/ThinkPowerProtocol.java b/src/main/java/org/traccar/protocol/ThinkPowerProtocol.java
index ece1b0544..38bf078aa 100644
--- a/src/main/java/org/traccar/protocol/ThinkPowerProtocol.java
+++ b/src/main/java/org/traccar/protocol/ThinkPowerProtocol.java
@@ -19,13 +19,17 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class ThinkPowerProtocol extends BaseProtocol {
- public ThinkPowerProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public ThinkPowerProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 2, 2, 2, 0));
pipeline.addLast(new ThinkPowerProtocolDecoder(ThinkPowerProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/ThinkPowerProtocolDecoder.java b/src/main/java/org/traccar/protocol/ThinkPowerProtocolDecoder.java
index b3f943078..e7ab23e5b 100644
--- a/src/main/java/org/traccar/protocol/ThinkPowerProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ThinkPowerProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,7 +19,8 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.helper.BufferUtil;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Checksum;
@@ -64,8 +65,8 @@ public class ThinkPowerProtocolDecoder extends BaseProtocolDecoder {
switch (type) {
case 0x01:
position.setValid(true);
- position.setLatitude(buf.readInt() * 0.0000001);
- position.setLongitude(buf.readInt() * 0.0000001);
+ position.setLatitude(BufferUtil.readSignedMagnitudeInt(buf) * 0.0000001);
+ position.setLongitude(BufferUtil.readSignedMagnitudeInt(buf) * 0.0000001);
position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort() * 0.1));
position.setCourse(buf.readUnsignedShort() * 0.01);
break;
diff --git a/src/main/java/org/traccar/protocol/ThinkRaceProtocol.java b/src/main/java/org/traccar/protocol/ThinkRaceProtocol.java
index ca1237cef..782b0a352 100644
--- a/src/main/java/org/traccar/protocol/ThinkRaceProtocol.java
+++ b/src/main/java/org/traccar/protocol/ThinkRaceProtocol.java
@@ -19,13 +19,17 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class ThinkRaceProtocol extends BaseProtocol {
- public ThinkRaceProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public ThinkRaceProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 2 + 12 + 1 + 1, 2, 2, 0));
pipeline.addLast(new ThinkRaceProtocolDecoder(ThinkRaceProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/ThinkRaceProtocolDecoder.java b/src/main/java/org/traccar/protocol/ThinkRaceProtocolDecoder.java
index 0928b25e0..796b726ea 100644
--- a/src/main/java/org/traccar/protocol/ThinkRaceProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ThinkRaceProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
@@ -104,7 +104,7 @@ public class ThinkRaceProtocolDecoder extends BaseProtocolDecoder {
position.setCourse(buf.readUnsignedByte());
position.setNetwork(new Network(
- CellTower.fromLacCid(buf.readUnsignedShort(), buf.readUnsignedShort())));
+ CellTower.fromLacCid(getConfig(), buf.readUnsignedShort(), buf.readUnsignedShort())));
return position;
diff --git a/src/main/java/org/traccar/protocol/ThurayaProtocol.java b/src/main/java/org/traccar/protocol/ThurayaProtocol.java
new file mode 100644
index 000000000..f709a1183
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ThurayaProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
+
+public class ThurayaProtocol extends BaseProtocol {
+
+ @Inject
+ public ThurayaProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 2, 2, -4, 0));
+ pipeline.addLast(new ThurayaProtocolDecoder(ThurayaProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/ThurayaProtocolDecoder.java b/src/main/java/org/traccar/protocol/ThurayaProtocolDecoder.java
new file mode 100644
index 000000000..a287ece34
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ThurayaProtocolDecoder.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.DateBuilder;
+import org.traccar.model.Position;
+import org.traccar.session.DeviceSession;
+
+import java.net.SocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.LinkedList;
+import java.util.List;
+
+public class ThurayaProtocolDecoder extends BaseProtocolDecoder {
+
+ public ThurayaProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_EVENT = 0x5101;
+ public static final int MSG_PERIODIC_REPORT = 0x7101;
+ public static final int MSG_SETTING_RESPONSE = 0x8115;
+ public static final int MSG_ACK = 0x9901;
+
+ private static int checksum(ByteBuffer buf) {
+ int crc = 0;
+ while (buf.hasRemaining()) {
+ crc += buf.get();
+ }
+ crc = ~crc;
+ crc += 1;
+ return crc;
+ }
+
+ private void sendResponse(Channel channel, SocketAddress remoteAddress, long id, int type) {
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeCharSequence("#T", StandardCharsets.US_ASCII);
+ response.writeShort(15); // length
+ response.writeShort(MSG_ACK);
+ response.writeInt((int) id);
+ response.writeShort(type);
+ response.writeShort(1); // server ok
+ response.writeShort(checksum(response.nioBuffer()));
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ }
+
+ private void decodeLocation(ByteBuf buf, Position position) {
+
+ position.setValid(true);
+
+ DateBuilder dateBuilder = new DateBuilder();
+
+ int date = buf.readInt();
+ dateBuilder.setDay(date % 100);
+ date /= 100;
+ dateBuilder.setMonth(date % 100);
+ date /= 100;
+ dateBuilder.setYear(date);
+
+ int time = buf.readInt();
+ dateBuilder.setSecond(time % 100);
+ time /= 100;
+ dateBuilder.setMinute(time % 100);
+ time /= 100;
+ dateBuilder.setHour(time);
+
+ position.setTime(dateBuilder.getDate());
+
+ position.setLongitude(buf.readInt() / 1000000.0);
+ position.setLatitude(buf.readInt() / 1000000.0);
+
+ int data = buf.readUnsignedShort();
+
+ int ignition = BitUtil.from(data, 12);
+ if (ignition == 1) {
+ position.set(Position.KEY_IGNITION, true);
+ } else if (ignition == 2) {
+ position.set(Position.KEY_IGNITION, false);
+ }
+
+ position.setCourse(BitUtil.to(data, 12));
+ position.setSpeed(buf.readShort());
+
+ position.set(Position.KEY_RPM, buf.readShort());
+
+ position.set("data", readString(buf));
+ }
+
+ private String decodeAlarm(int event) {
+ switch (event) {
+ case 10:
+ return Position.ALARM_VIBRATION;
+ case 11:
+ return Position.ALARM_OVERSPEED;
+ case 12:
+ return Position.ALARM_POWER_CUT;
+ case 13:
+ return Position.ALARM_LOW_BATTERY;
+ case 18:
+ return Position.ALARM_GPS_ANTENNA_CUT;
+ case 20:
+ return Position.ALARM_ACCELERATION;
+ case 21:
+ return Position.ALARM_BRAKING;
+ default:
+ return null;
+ }
+ }
+
+ private String readString(ByteBuf buf) {
+ int endIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 0);
+ CharSequence value = buf.readCharSequence(endIndex - buf.readerIndex(), StandardCharsets.US_ASCII);
+ buf.readUnsignedByte(); // delimiter
+ return value.toString();
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.skipBytes(2); // service
+ buf.readUnsignedShort(); // length
+ int type = buf.readUnsignedShort();
+ long id = buf.readUnsignedInt();
+
+ sendResponse(channel, remoteAddress, id, type);
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(id));
+ if (deviceSession == null) {
+ return null;
+ }
+
+ if (type == MSG_EVENT) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ decodeLocation(buf, position);
+
+ int event = buf.readUnsignedByte();
+ position.set(Position.KEY_ALARM, decodeAlarm(event));
+ position.set(Position.KEY_EVENT, event);
+ position.set("eventData", readString(buf));
+
+ return position;
+
+ } else if (type == MSG_PERIODIC_REPORT) {
+
+ List<Position> positions = new LinkedList<>();
+
+ int count = buf.readUnsignedByte();
+ for (int i = 0; i < count; i++) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ decodeLocation(buf, position);
+
+ positions.add(position);
+
+ }
+
+ return positions;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Tk102Protocol.java b/src/main/java/org/traccar/protocol/Tk102Protocol.java
index 9f2463cd6..150e83ab3 100644
--- a/src/main/java/org/traccar/protocol/Tk102Protocol.java
+++ b/src/main/java/org/traccar/protocol/Tk102Protocol.java
@@ -19,13 +19,17 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class Tk102Protocol extends BaseProtocol {
- public Tk102Protocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public Tk102Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 1 + 1 + 10, 1, 1, 0));
pipeline.addLast(new Tk102ProtocolDecoder(Tk102Protocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/Tk102ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tk102ProtocolDecoder.java
index da0c6928b..af29fbc21 100644
--- a/src/main/java/org/traccar/protocol/Tk102ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Tk102ProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
diff --git a/src/main/java/org/traccar/protocol/Tk103Protocol.java b/src/main/java/org/traccar/protocol/Tk103Protocol.java
index ff0bedfb7..cf09886f5 100644
--- a/src/main/java/org/traccar/protocol/Tk103Protocol.java
+++ b/src/main/java/org/traccar/protocol/Tk103Protocol.java
@@ -21,11 +21,15 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class Tk103Protocol extends BaseProtocol {
- public Tk103Protocol() {
+ @Inject
+ public Tk103Protocol(Config config) {
setSupportedDataCommands(
Command.TYPE_CUSTOM,
Command.TYPE_GET_DEVICE_STATUS,
@@ -45,9 +49,9 @@ public class Tk103Protocol extends BaseProtocol {
Command.TYPE_ENGINE_STOP,
Command.TYPE_ENGINE_RESUME,
Command.TYPE_OUTPUT_CONTROL);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new Tk103FrameDecoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
@@ -55,9 +59,9 @@ public class Tk103Protocol extends BaseProtocol {
pipeline.addLast(new Tk103ProtocolDecoder(Tk103Protocol.this));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new Tk103ProtocolEncoder(Tk103Protocol.this));
diff --git a/src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java
index ff33cb103..b343c3b33 100644
--- a/src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.config.Keys;
@@ -36,11 +35,15 @@ import java.util.regex.Pattern;
public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
- private final boolean decodeLow;
+ private boolean decodeLow;
public Tk103ProtocolDecoder(Protocol protocol) {
super(protocol);
- decodeLow = Context.getConfig().getBoolean(Keys.PROTOCOL_DECODE_LOW.withPrefix(getProtocolName()));
+ }
+
+ @Override
+ protected void init() {
+ decodeLow = getConfig().getBoolean(Keys.PROTOCOL_DECODE_LOW.withPrefix(getProtocolName()));
}
private static final Pattern PATTERN = new PatternBuilder()
@@ -96,6 +99,16 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
.any()
.compile();
+ private static final Pattern PATTERN_CELL = new PatternBuilder()
+ .text("(")
+ .number("(d{12})") // device id
+ .expression(".{4}") // type
+ .number("(?:d{15})?,") // imei
+ .expression("(.+),") // cell
+ .number("(d{8})") // odometer
+ .text(")")
+ .compile();
+
private static final Pattern PATTERN_NETWORK = new PatternBuilder()
.text("(").optional()
.number("(d{12})") // device id
@@ -294,6 +307,39 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private Position decodeCell(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_CELL, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ Network network = new Network();
+
+ String[] cells = parser.next().split("\n");
+ for (String cell : cells) {
+ String[] values = cell.substring(1, cell.length() - 1).split(",");
+ network.addCellTower(CellTower.from(
+ Integer.parseInt(values[0]), Integer.parseInt(values[1]),
+ Integer.parseInt(values[2]), Integer.parseInt(values[3])));
+ }
+
+ position.setNetwork(network);
+
+ position.set(Position.KEY_ODOMETER, parser.nextLong(16, 0));
+
+ return position;
+ }
+
private Position decodeNetwork(Channel channel, SocketAddress remoteAddress, String sentence) {
Parser parser = new Parser(PATTERN_NETWORK, sentence);
if (!parser.matches()) {
@@ -419,7 +465,9 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
}
}
- if (sentence.contains("ZC20")) {
+ if (sentence.indexOf('{') > 0 && sentence.indexOf('}') > 0) {
+ return decodeCell(channel, remoteAddress, sentence);
+ } else if (sentence.contains("ZC20")) {
return decodeBattery(channel, remoteAddress, sentence);
} else if (sentence.contains("BZ00")) {
return decodeNetwork(channel, remoteAddress, sentence);
diff --git a/src/main/java/org/traccar/protocol/Tk103ProtocolEncoder.java b/src/main/java/org/traccar/protocol/Tk103ProtocolEncoder.java
index 5d7e63920..e3e1ae961 100644
--- a/src/main/java/org/traccar/protocol/Tk103ProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/Tk103ProtocolEncoder.java
@@ -16,10 +16,11 @@
*/
package org.traccar.protocol;
-import org.traccar.Context;
+import org.traccar.Protocol;
import org.traccar.StringProtocolEncoder;
+import org.traccar.config.Keys;
+import org.traccar.helper.model.AttributeUtil;
import org.traccar.model.Command;
-import org.traccar.Protocol;
public class Tk103ProtocolEncoder extends StringProtocolEncoder {
@@ -42,12 +43,12 @@ public class Tk103ProtocolEncoder extends StringProtocolEncoder {
@Override
protected Object encodeCommand(Command command) {
- boolean alternative = forceAlternative || Context.getIdentityManager().lookupAttributeBoolean(
- command.getDeviceId(), getProtocolName() + ".alternative", false, false, true);
+ boolean alternative = AttributeUtil.lookup(
+ getCacheManager(), Keys.PROTOCOL_ALTERNATIVE.withPrefix(getProtocolName()), command.getDeviceId());
initDevicePassword(command, "123456");
- if (alternative) {
+ if (alternative || forceAlternative) {
switch (command.getType()) {
case Command.TYPE_CUSTOM:
return formatAlt(command, "%s", Command.KEY_DATA);
diff --git a/src/main/java/org/traccar/protocol/Tlt2hProtocol.java b/src/main/java/org/traccar/protocol/Tlt2hProtocol.java
index 12fd92afa..b10271f7d 100644
--- a/src/main/java/org/traccar/protocol/Tlt2hProtocol.java
+++ b/src/main/java/org/traccar/protocol/Tlt2hProtocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class Tlt2hProtocol extends BaseProtocol {
- public Tlt2hProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public Tlt2hProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(32 * 1024, "##\r\n"));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java
index ad7dfa886..e85bdf9b3 100644
--- a/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,8 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.model.CellTower;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
@@ -55,6 +56,12 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder {
private static final Pattern PATTERN_POSITION = new PatternBuilder()
.text("#")
.number("(?:(dd)|x*)") // cell or voltage
+ .groupBegin()
+ .number("#(d+),") // mcc
+ .number("(d+),") // mnc
+ .number("(x+),") // lac
+ .number("(x+)") // cell id
+ .groupEnd("?")
.text("$GPRMC,")
.number("(dd)(dd)(dd).d+,") // time (hhmmss.sss)
.expression("([AVL]),") // validity
@@ -71,6 +78,12 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder {
private static final Pattern PATTERN_WIFI = new PatternBuilder()
.text("#")
.number("(?:(dd)|x+)") // cell or voltage
+ .groupBegin()
+ .number("#(d+),") // mcc
+ .number("(d+),") // mnc
+ .number("(x+),") // lac
+ .number("(x+)") // cell id
+ .groupEnd("?")
.text("$WIFI,")
.number("(dd)(dd)(dd).d+,") // time (hhmmss.sss)
.expression("[AVL],") // validity
@@ -168,6 +181,13 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_BATTERY, parser.nextInt() * 0.1);
}
+ if (parser.hasNext(4)) {
+ Network network = new Network();
+ network.addCellTower(CellTower.from(
+ parser.nextInt(), parser.nextInt(), parser.nextHexInt(), parser.nextHexInt()));
+ position.setNetwork(network);
+ }
+
DateBuilder dateBuilder = new DateBuilder()
.setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
@@ -191,11 +211,16 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_BATTERY, parser.nextInt() * 0.1);
+ Network network = new Network();
+ if (parser.hasNext(4)) {
+ network.addCellTower(CellTower.from(
+ parser.nextInt(), parser.nextInt(), parser.nextHexInt(), parser.nextHexInt()));
+ }
+
DateBuilder dateBuilder = new DateBuilder()
.setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
String[] values = parser.next().split(",");
- Network network = new Network();
for (int i = 0; i < values.length / 2; i++) {
String mac = values[i * 2 + 1].replaceAll("(..)", "$1:").substring(0, 17);
network.addWifiAccessPoint(WifiAccessPoint.from(mac, Integer.parseInt(values[i * 2])));
diff --git a/src/main/java/org/traccar/protocol/TlvProtocol.java b/src/main/java/org/traccar/protocol/TlvProtocol.java
index 94f5da94f..9d83388c9 100644
--- a/src/main/java/org/traccar/protocol/TlvProtocol.java
+++ b/src/main/java/org/traccar/protocol/TlvProtocol.java
@@ -19,13 +19,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class TlvProtocol extends BaseProtocol {
- public TlvProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public TlvProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '\0'));
pipeline.addLast(new TlvProtocolDecoder(TlvProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/TlvProtocolDecoder.java b/src/main/java/org/traccar/protocol/TlvProtocolDecoder.java
index 36cf7859f..7870c778a 100644
--- a/src/main/java/org/traccar/protocol/TlvProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TlvProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.UnitsConverter;
diff --git a/src/main/java/org/traccar/protocol/TmgProtocol.java b/src/main/java/org/traccar/protocol/TmgProtocol.java
index 020332ce7..e078c425b 100644
--- a/src/main/java/org/traccar/protocol/TmgProtocol.java
+++ b/src/main/java/org/traccar/protocol/TmgProtocol.java
@@ -20,13 +20,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class TmgProtocol extends BaseProtocol {
- public TmgProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public TmgProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new TmgFrameDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/TmgProtocolDecoder.java b/src/main/java/org/traccar/protocol/TmgProtocolDecoder.java
index d27849f8c..00dc2a09b 100644
--- a/src/main/java/org/traccar/protocol/TmgProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TmgProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/TopflytechProtocol.java b/src/main/java/org/traccar/protocol/TopflytechProtocol.java
index 303072bdb..339d2fc8d 100644
--- a/src/main/java/org/traccar/protocol/TopflytechProtocol.java
+++ b/src/main/java/org/traccar/protocol/TopflytechProtocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class TopflytechProtocol extends BaseProtocol {
- public TopflytechProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public TopflytechProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ')'));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/TopflytechProtocolDecoder.java b/src/main/java/org/traccar/protocol/TopflytechProtocolDecoder.java
index 6de053c32..92a7b5c9d 100644
--- a/src/main/java/org/traccar/protocol/TopflytechProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TopflytechProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/TopinProtocol.java b/src/main/java/org/traccar/protocol/TopinProtocol.java
index d28afbf94..b15373d71 100644
--- a/src/main/java/org/traccar/protocol/TopinProtocol.java
+++ b/src/main/java/org/traccar/protocol/TopinProtocol.java
@@ -18,16 +18,20 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class TopinProtocol extends BaseProtocol {
- public TopinProtocol() {
+ @Inject
+ public TopinProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_SOS_NUMBER);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new TopinProtocolEncoder(TopinProtocol.this));
pipeline.addLast(new TopinProtocolDecoder(TopinProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java b/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java
index 267bce562..a1d5481db 100644
--- a/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java
@@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BcdUtil;
@@ -93,6 +93,19 @@ public class TopinProtocolDecoder extends BaseProtocolDecoder {
return negative ? -result : result;
}
+ private String decodeAlarm(int alarms) {
+ if (BitUtil.check(alarms, 0)) {
+ return Position.ALARM_VIBRATION;
+ }
+ if (BitUtil.check(alarms, 1)) {
+ return Position.ALARM_OVERSPEED;
+ }
+ if (BitUtil.check(alarms, 4)) {
+ return Position.ALARM_LOW_POWER;
+ }
+ return null;
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -158,17 +171,7 @@ public class TopinProtocolDecoder extends BaseProtocolDecoder {
if (buf.readableBytes() >= 5) {
position.setAltitude(buf.readShort());
-
- int alarms = buf.readUnsignedByte();
- if (BitUtil.check(alarms, 0)) {
- position.set(Position.KEY_ALARM, Position.ALARM_VIBRATION);
- }
- if (BitUtil.check(alarms, 1)) {
- position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED);
- }
- if (BitUtil.check(alarms, 4)) {
- position.set(Position.KEY_ALARM, Position.ALARM_LOW_POWER);
- }
+ position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
}
ByteBuf content = Unpooled.buffer();
@@ -190,10 +193,12 @@ public class TopinProtocolDecoder extends BaseProtocolDecoder {
getLastLocation(position, null);
+ ByteBuf content = buf.retainedSlice(buf.readerIndex(), buf.readableBytes() - 2);
+
position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
position.set(Position.KEY_VERSION_FW, buf.readUnsignedByte());
buf.readUnsignedByte(); // timezone
- int interval = buf.readUnsignedByte();
+ buf.readUnsignedByte(); // interval
if (buf.readableBytes() >= 1 + 2) {
position.set(Position.KEY_RSSI, buf.readUnsignedByte());
}
@@ -207,8 +212,6 @@ public class TopinProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_HEART_RATE, buf.readUnsignedByte());
}
- ByteBuf content = Unpooled.buffer();
- content.writeByte(interval);
sendResponse(channel, length, type, content);
return position;
@@ -246,6 +249,10 @@ public class TopinProtocolDecoder extends BaseProtocolDecoder {
mcc, mnc, buf.readUnsignedShort(), buf.readUnsignedShort(), buf.readUnsignedByte()));
}
+ if (buf.readableBytes() > 2) {
+ position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
+ }
+
position.setNetwork(network);
ByteBuf content = Unpooled.buffer();
diff --git a/src/main/java/org/traccar/protocol/TotemProtocol.java b/src/main/java/org/traccar/protocol/TotemProtocol.java
index f8cda8358..9ab36fd0b 100644
--- a/src/main/java/org/traccar/protocol/TotemProtocol.java
+++ b/src/main/java/org/traccar/protocol/TotemProtocol.java
@@ -20,18 +20,39 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class TotemProtocol extends BaseProtocol {
- public TotemProtocol() {
+ @Inject
+ public TotemProtocol(Config config) {
setSupportedDataCommands(
+ Command.TYPE_CUSTOM,
+ Command.TYPE_REBOOT_DEVICE,
+ Command.TYPE_FACTORY_RESET,
+ Command.TYPE_GET_VERSION,
+ Command.TYPE_POSITION_SINGLE,
+ Command.TYPE_ENGINE_RESUME,
+ Command.TYPE_ENGINE_STOP
+ );
+
+ setTextCommandEncoder(new TotemProtocolSmsEncoder(this));
+ setSupportedTextCommands(
+ Command.TYPE_CUSTOM,
+ Command.TYPE_REBOOT_DEVICE,
+ Command.TYPE_FACTORY_RESET,
+ Command.TYPE_GET_VERSION,
+ Command.TYPE_POSITION_SINGLE,
Command.TYPE_ENGINE_RESUME,
Command.TYPE_ENGINE_STOP
);
- addServer(new TrackerServer(false, getName()) {
+
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new TotemFrameDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/TotemProtocolDecoder.java b/src/main/java/org/traccar/protocol/TotemProtocolDecoder.java
index 58c66031e..9d0d794f8 100644
--- a/src/main/java/org/traccar/protocol/TotemProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TotemProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
@@ -140,8 +140,12 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
.number("(x{8})") // status
.number("(dd)(dd)(dd)") // date (yymmdd)
.number("(dd)(dd)(dd)") // time (hhmmss)
+ .groupBegin()
.number("(dd)") // battery
.number("(dd)") // external power
+ .or()
+ .number("(ddd)") // battery
+ .groupEnd()
.number("(dddd)") // adc 1
.groupBegin()
.groupBegin()
@@ -166,6 +170,7 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
.number("(d{7})") // odometer
.number("(dd)(dd.dddd)([NS])") // latitude
.number("(ddd)(dd.dddd)([EW])") // longitude
+ .number("dddd").optional() // temperature
.number("dddd") // serial number
.number("xx") // checksum
.any()
@@ -320,7 +325,7 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
int lac = parser.nextHexInt(0);
int cid = parser.nextHexInt(0);
if (lac != 0 && cid != 0) {
- position.setNetwork(new Network(CellTower.fromLacCid(lac, cid)));
+ position.setNetwork(new Network(CellTower.fromLacCid(getConfig(), lac, cid)));
}
position.set(Position.PREFIX_TEMP + 1, parser.next());
@@ -346,7 +351,7 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.PREFIX_TEMP + 2, parser.next());
position.setNetwork(new Network(
- CellTower.fromLacCid(parser.nextHexInt(0), parser.nextHexInt(0))));
+ CellTower.fromLacCid(getConfig(), parser.nextHexInt(0), parser.nextHexInt(0))));
position.setValid(parser.next().equals("A"));
position.set(Position.KEY_SATELLITES, parser.nextInt());
@@ -379,8 +384,13 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
position.setTime(parser.nextDateTime());
- position.set(Position.KEY_BATTERY, parser.nextDouble() * 0.1);
- position.set(Position.KEY_POWER, parser.nextDouble());
+ if (parser.hasNext(2)) {
+ position.set(Position.KEY_BATTERY, parser.nextDouble() * 0.1);
+ position.set(Position.KEY_POWER, parser.nextDouble());
+ }
+ if (parser.hasNext()) {
+ position.set(Position.KEY_BATTERY, parser.nextDouble() * 0.01);
+ }
position.set(Position.PREFIX_ADC + 1, parser.next());
position.set(Position.PREFIX_ADC + 2, parser.next());
@@ -403,7 +413,7 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
int mcc = parser.nextInt();
cellTower = CellTower.from(mcc, mnc, lac, cid);
} else {
- cellTower = CellTower.fromLacCid(lac, cid);
+ cellTower = CellTower.fromLacCid(getConfig(), lac, cid);
}
position.set(Position.KEY_SATELLITES, parser.nextInt());
cellTower.setSignalStrength(parser.nextInt());
diff --git a/src/main/java/org/traccar/protocol/TotemProtocolEncoder.java b/src/main/java/org/traccar/protocol/TotemProtocolEncoder.java
index a96dd1ee3..4b22ade03 100644
--- a/src/main/java/org/traccar/protocol/TotemProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/TotemProtocolEncoder.java
@@ -6,7 +6,7 @@
* 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
+ * 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,
@@ -19,6 +19,7 @@ package org.traccar.protocol;
import org.traccar.StringProtocolEncoder;
import org.traccar.model.Command;
import org.traccar.Protocol;
+import org.traccar.helper.Checksum;
public class TotemProtocolEncoder extends StringProtocolEncoder {
@@ -26,20 +27,41 @@ public class TotemProtocolEncoder extends StringProtocolEncoder {
super(protocol);
}
- @Override
- protected Object encodeCommand(Command command) {
-
- initDevicePassword(command, "000000");
-
+ public static String formatContent(Command command) {
switch (command.getType()) {
+ case Command.TYPE_CUSTOM:
+ return String.format("%s,%s",
+ command.getAttributes().get(Command.KEY_DEVICE_PASSWORD),
+ command.getAttributes().get(Command.KEY_DATA)
+ );
+ case Command.TYPE_REBOOT_DEVICE:
+ return String.format("%s,006", command.getAttributes().get(Command.KEY_DEVICE_PASSWORD));
+ case Command.TYPE_FACTORY_RESET:
+ return String.format("%s,007", command.getAttributes().get(Command.KEY_DEVICE_PASSWORD));
+ case Command.TYPE_GET_VERSION:
+ return String.format("%s,056", command.getAttributes().get(Command.KEY_DEVICE_PASSWORD));
+ case Command.TYPE_POSITION_SINGLE:
+ return String.format("%s,012", command.getAttributes().get(Command.KEY_DEVICE_PASSWORD));
// Assuming PIN 8 (Output C) is the power wire, like manual says but it can be PIN 5,7,8
case Command.TYPE_ENGINE_STOP:
- return formatCommand(command, "*%s,025,C,1#", Command.KEY_DEVICE_PASSWORD);
+ return String.format("%s,025,C,1", command.getAttributes().get(Command.KEY_DEVICE_PASSWORD));
case Command.TYPE_ENGINE_RESUME:
- return formatCommand(command, "*%s,025,C,0#", Command.KEY_DEVICE_PASSWORD);
+ return String.format("%s,025,C,0", command.getAttributes().get(Command.KEY_DEVICE_PASSWORD));
default:
return null;
}
}
+ @Override
+ protected Object encodeCommand(Command command) {
+
+ initDevicePassword(command, "000000");
+
+ String commandString = formatContent(command);
+ String builtCommand = String.format("$$%04dCF%s", 10 + commandString.getBytes().length, commandString);
+
+ return String.format("%s%02X", builtCommand, Checksum.xor(builtCommand));
+
+ }
+
}
diff --git a/src/main/java/org/traccar/protocol/TotemProtocolSmsEncoder.java b/src/main/java/org/traccar/protocol/TotemProtocolSmsEncoder.java
new file mode 100644
index 000000000..8656f8a5c
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TotemProtocolSmsEncoder.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2015 Irving Gonzalez
+ * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import org.traccar.StringProtocolEncoder;
+import org.traccar.model.Command;
+import org.traccar.Protocol;
+
+public class TotemProtocolSmsEncoder extends StringProtocolEncoder {
+
+ public TotemProtocolSmsEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object encodeCommand(Command command) {
+
+ initDevicePassword(command, "000000");
+
+ return String.format("*%s#", TotemProtocolEncoder.formatContent(command));
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Tr20Protocol.java b/src/main/java/org/traccar/protocol/Tr20Protocol.java
index 1b71db03f..615fdab28 100644
--- a/src/main/java/org/traccar/protocol/Tr20Protocol.java
+++ b/src/main/java/org/traccar/protocol/Tr20Protocol.java
@@ -21,22 +21,26 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class Tr20Protocol extends BaseProtocol {
- public Tr20Protocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public Tr20Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new Tr20ProtocolDecoder(Tr20Protocol.this));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new Tr20ProtocolDecoder(Tr20Protocol.this));
diff --git a/src/main/java/org/traccar/protocol/Tr20ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tr20ProtocolDecoder.java
index 2f11bd152..0e1c7568b 100644
--- a/src/main/java/org/traccar/protocol/Tr20ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Tr20ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
@@ -52,7 +52,7 @@ public class Tr20ProtocolDecoder extends BaseProtocolDecoder {
.number("(ddd)(dd.d+),") // longitude
.number("(d+),") // speed
.number("(d+),") // course
- .number("(?:NA|[FC]?(-?d+)[^,]*),") // temperature
+ .number("(?:NA|[BFC]?(-?d+)[^,]*),") // temperature
.number("(x{8}),") // status
.number("(d+)") // event
.any()
diff --git a/src/main/java/org/traccar/protocol/Tr900Protocol.java b/src/main/java/org/traccar/protocol/Tr900Protocol.java
index b70521b35..162cbe651 100644
--- a/src/main/java/org/traccar/protocol/Tr900Protocol.java
+++ b/src/main/java/org/traccar/protocol/Tr900Protocol.java
@@ -21,22 +21,26 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class Tr900Protocol extends BaseProtocol {
- public Tr900Protocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public Tr900Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new Tr900ProtocolDecoder(Tr900Protocol.this));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new Tr900ProtocolDecoder(Tr900Protocol.this));
diff --git a/src/main/java/org/traccar/protocol/Tr900ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tr900ProtocolDecoder.java
index 319194c21..da0e8d292 100644
--- a/src/main/java/org/traccar/protocol/Tr900ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Tr900ProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/TrackboxProtocol.java b/src/main/java/org/traccar/protocol/TrackboxProtocol.java
index 5da5abd64..4236144a3 100644
--- a/src/main/java/org/traccar/protocol/TrackboxProtocol.java
+++ b/src/main/java/org/traccar/protocol/TrackboxProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class TrackboxProtocol extends BaseProtocol {
- public TrackboxProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public TrackboxProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/TrackboxProtocolDecoder.java b/src/main/java/org/traccar/protocol/TrackboxProtocolDecoder.java
index db8022738..10483d445 100644
--- a/src/main/java/org/traccar/protocol/TrackboxProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TrackboxProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
diff --git a/src/main/java/org/traccar/protocol/TrakMateProtocol.java b/src/main/java/org/traccar/protocol/TrakMateProtocol.java
index bda5df10f..b7637e6f3 100644
--- a/src/main/java/org/traccar/protocol/TrakMateProtocol.java
+++ b/src/main/java/org/traccar/protocol/TrakMateProtocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class TrakMateProtocol extends BaseProtocol {
- public TrakMateProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public TrakMateProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '#'));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/TrakMateProtocolDecoder.java b/src/main/java/org/traccar/protocol/TrakMateProtocolDecoder.java
index 4d5cb18f5..b1f50dc10 100644
--- a/src/main/java/org/traccar/protocol/TrakMateProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TrakMateProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/TramigoProtocol.java b/src/main/java/org/traccar/protocol/TramigoProtocol.java
index f683ccc5d..79a59abd3 100644
--- a/src/main/java/org/traccar/protocol/TramigoProtocol.java
+++ b/src/main/java/org/traccar/protocol/TramigoProtocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class TramigoProtocol extends BaseProtocol {
- public TramigoProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public TramigoProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new TramigoFrameDecoder());
pipeline.addLast(new TramigoProtocolDecoder(TramigoProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/TramigoProtocolDecoder.java b/src/main/java/org/traccar/protocol/TramigoProtocolDecoder.java
index e42e2f670..21dd78da3 100644
--- a/src/main/java/org/traccar/protocol/TramigoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TramigoProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.DateUtil;
diff --git a/src/main/java/org/traccar/protocol/TrvProtocol.java b/src/main/java/org/traccar/protocol/TrvProtocol.java
index 99a164cf1..e67afbda2 100644
--- a/src/main/java/org/traccar/protocol/TrvProtocol.java
+++ b/src/main/java/org/traccar/protocol/TrvProtocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class TrvProtocol extends BaseProtocol {
- public TrvProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public TrvProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '#'));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java b/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java
index 05312b820..9df29ae1b 100644
--- a/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
@@ -59,7 +59,7 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
.number("(d)") // acc
.number("(dd)") // arm status
.number("(dd)") // working mode
- .number("(?:[0-2]{3})?,")
+ .number("(?:d{3,5})?,")
.number("(d+),") // mcc
.number("(d+),") // mnc
.number("(d+),") // lac
@@ -183,7 +183,7 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
return position;
- } else if (type.equals("AP01") || type.equals("AP10") || type.equals("YP03")) {
+ } else if (type.equals("AP01") || type.equals("AP10") || type.equals("YP03") || type.equals("YP14")) {
Parser parser = new Parser(PATTERN, sentence);
if (!parser.matches()) {
diff --git a/src/main/java/org/traccar/protocol/Tt8850Protocol.java b/src/main/java/org/traccar/protocol/Tt8850Protocol.java
index 66a13da9e..ab109e274 100644
--- a/src/main/java/org/traccar/protocol/Tt8850Protocol.java
+++ b/src/main/java/org/traccar/protocol/Tt8850Protocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class Tt8850Protocol extends BaseProtocol {
- public Tt8850Protocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public Tt8850Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "$"));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/Tt8850ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tt8850ProtocolDecoder.java
index 1010528c4..cbc983000 100644
--- a/src/main/java/org/traccar/protocol/Tt8850ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Tt8850ProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/TytanProtocol.java b/src/main/java/org/traccar/protocol/TytanProtocol.java
index 32e9acae1..cc3bc9b52 100644
--- a/src/main/java/org/traccar/protocol/TytanProtocol.java
+++ b/src/main/java/org/traccar/protocol/TytanProtocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class TytanProtocol extends BaseProtocol {
- public TytanProtocol() {
- addServer(new TrackerServer(true, getName()) {
+ @Inject
+ public TytanProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new TytanProtocolDecoder(TytanProtocol.this));
}
});
diff --git a/src/main/java/org/traccar/protocol/TytanProtocolDecoder.java b/src/main/java/org/traccar/protocol/TytanProtocolDecoder.java
index 93d3a63d2..6169e0545 100644
--- a/src/main/java/org/traccar/protocol/TytanProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TytanProtocolDecoder.java
@@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/TzoneProtocol.java b/src/main/java/org/traccar/protocol/TzoneProtocol.java
index 6e855d138..d25757b63 100644
--- a/src/main/java/org/traccar/protocol/TzoneProtocol.java
+++ b/src/main/java/org/traccar/protocol/TzoneProtocol.java
@@ -15,18 +15,21 @@
*/
package org.traccar.protocol;
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
-import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import javax.inject.Inject;
public class TzoneProtocol extends BaseProtocol {
- public TzoneProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public TzoneProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(256, 2, 2, 2, 0));
pipeline.addLast(new TzoneProtocolDecoder(TzoneProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java b/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java
index 249915b39..8e84a6781 100644
--- a/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java
@@ -17,10 +17,13 @@ package org.traccar.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
+import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
+import org.traccar.NetworkMessage;
import org.traccar.Protocol;
+import org.traccar.config.Keys;
import org.traccar.helper.BcdUtil;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
@@ -30,6 +33,11 @@ import org.traccar.model.Network;
import org.traccar.model.Position;
import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
public class TzoneProtocolDecoder extends BaseProtocolDecoder {
@@ -37,6 +45,20 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder {
super(protocol);
}
+ private void sendResponse(Channel channel, SocketAddress remoteAddress, int index) {
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+
+ String ack = String.format("@ACK,%d#", index);
+ String time = String.format("@UTC time:%s", dateFormat.format(new Date()));
+
+ ByteBuf response = Unpooled.copiedBuffer(ack + time, StandardCharsets.US_ASCII);
+
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ }
+
private String decodeAlarm(Short value) {
switch (value) {
case 0x01:
@@ -261,7 +283,7 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder {
if (hardware == 0x10A || hardware == 0x10B || hardware == 0x406) {
position.setNetwork(new Network(
- CellTower.fromLacCid(buf.readUnsignedShort(), buf.readUnsignedShort())));
+ CellTower.fromLacCid(getConfig(), buf.readUnsignedShort(), buf.readUnsignedShort())));
} else if (hardware == 0x407) {
@@ -348,6 +370,10 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder {
}
+ if (getConfig().getBoolean(Keys.PROTOCOL_ACK.withPrefix(getProtocolName()))) {
+ sendResponse(channel, remoteAddress, buf.getUnsignedShort(buf.writerIndex() - 6));
+ }
+
return position;
}
diff --git a/src/main/java/org/traccar/protocol/UlbotechProtocol.java b/src/main/java/org/traccar/protocol/UlbotechProtocol.java
index dfe5235f0..57fc47644 100644
--- a/src/main/java/org/traccar/protocol/UlbotechProtocol.java
+++ b/src/main/java/org/traccar/protocol/UlbotechProtocol.java
@@ -18,16 +18,20 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class UlbotechProtocol extends BaseProtocol {
- public UlbotechProtocol() {
+ @Inject
+ public UlbotechProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_CUSTOM);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new UlbotechFrameDecoder());
pipeline.addLast(new UlbotechProtocolEncoder(UlbotechProtocol.this));
pipeline.addLast(new UlbotechProtocolDecoder(UlbotechProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/UlbotechProtocolDecoder.java b/src/main/java/org/traccar/protocol/UlbotechProtocolDecoder.java
index 7fec0bf8b..c9b35158e 100644
--- a/src/main/java/org/traccar/protocol/UlbotechProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/UlbotechProtocolDecoder.java
@@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
@@ -37,6 +37,7 @@ import org.traccar.model.Position;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.Date;
+import java.util.TimeZone;
import java.util.regex.Pattern;
public class UlbotechProtocolDecoder extends BaseProtocolDecoder {
@@ -214,16 +215,17 @@ public class UlbotechProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- if (deviceSession.getTimeZone() == null) {
- deviceSession.setTimeZone(getTimeZone(deviceSession.getDeviceId()));
+ if (!deviceSession.contains(DeviceSession.KEY_TIMEZONE)) {
+ deviceSession.set(DeviceSession.KEY_TIMEZONE, getTimeZone(deviceSession.getDeviceId()));
}
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
+ TimeZone timeZone = deviceSession.get(DeviceSession.KEY_TIMEZONE);
long seconds = buf.readUnsignedInt() & 0x7fffffffL;
seconds += 946684800L; // 2000-01-01 00:00
- seconds -= deviceSession.getTimeZone().getRawOffset() / 1000;
+ seconds -= timeZone.getRawOffset() / 1000;
Date time = new Date(seconds * 1000);
boolean hasLocation = false;
diff --git a/src/main/java/org/traccar/protocol/UproProtocol.java b/src/main/java/org/traccar/protocol/UproProtocol.java
index 4e60ffeb6..e27088594 100644
--- a/src/main/java/org/traccar/protocol/UproProtocol.java
+++ b/src/main/java/org/traccar/protocol/UproProtocol.java
@@ -20,13 +20,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class UproProtocol extends BaseProtocol {
- public UproProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public UproProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '#'));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new UproProtocolDecoder(UproProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/UproProtocolDecoder.java b/src/main/java/org/traccar/protocol/UproProtocolDecoder.java
index 9f236a7e5..ed714e464 100644
--- a/src/main/java/org/traccar/protocol/UproProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/UproProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
@@ -67,12 +67,11 @@ public class UproProtocolDecoder extends BaseProtocolDecoder {
DateBuilder dateBuilder = new DateBuilder()
.setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
- position.setValid(true);
position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_MIN_MIN));
position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_MIN_MIN));
int flags = parser.nextInt(0);
- position.setValid(BitUtil.check(flags, 0));
+ position.setValid(!BitUtil.check(flags, 0));
if (!BitUtil.check(flags, 1)) {
position.setLatitude(-position.getLatitude());
}
diff --git a/src/main/java/org/traccar/protocol/UuxProtocol.java b/src/main/java/org/traccar/protocol/UuxProtocol.java
index 41b59d829..3de4a4732 100644
--- a/src/main/java/org/traccar/protocol/UuxProtocol.java
+++ b/src/main/java/org/traccar/protocol/UuxProtocol.java
@@ -19,13 +19,17 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class UuxProtocol extends BaseProtocol {
- public UuxProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public UuxProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 3, 1));
pipeline.addLast(new UuxProtocolDecoder(UuxProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/UuxProtocolDecoder.java b/src/main/java/org/traccar/protocol/UuxProtocolDecoder.java
index 1081fa811..b9065e7f3 100644
--- a/src/main/java/org/traccar/protocol/UuxProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/UuxProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/V680Protocol.java b/src/main/java/org/traccar/protocol/V680Protocol.java
index dc0922cd4..53bca849c 100644
--- a/src/main/java/org/traccar/protocol/V680Protocol.java
+++ b/src/main/java/org/traccar/protocol/V680Protocol.java
@@ -21,22 +21,26 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class V680Protocol extends BaseProtocol {
- public V680Protocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public V680Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "##"));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new V680ProtocolDecoder(V680Protocol.this));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new V680ProtocolDecoder(V680Protocol.this));
diff --git a/src/main/java/org/traccar/protocol/V680ProtocolDecoder.java b/src/main/java/org/traccar/protocol/V680ProtocolDecoder.java
index 40267022b..237aea39b 100644
--- a/src/main/java/org/traccar/protocol/V680ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/V680ProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/VisiontekProtocol.java b/src/main/java/org/traccar/protocol/VisiontekProtocol.java
index 2c6af45a8..5296402b4 100644
--- a/src/main/java/org/traccar/protocol/VisiontekProtocol.java
+++ b/src/main/java/org/traccar/protocol/VisiontekProtocol.java
@@ -21,13 +21,17 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class VisiontekProtocol extends BaseProtocol {
- public VisiontekProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public VisiontekProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '#'));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/VisiontekProtocolDecoder.java b/src/main/java/org/traccar/protocol/VisiontekProtocolDecoder.java
index c4787bda2..9ab871bfe 100644
--- a/src/main/java/org/traccar/protocol/VisiontekProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/VisiontekProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/VnetProtocol.java b/src/main/java/org/traccar/protocol/VnetProtocol.java
index 0fed30e13..dd739f0d9 100644
--- a/src/main/java/org/traccar/protocol/VnetProtocol.java
+++ b/src/main/java/org/traccar/protocol/VnetProtocol.java
@@ -19,15 +19,19 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import java.nio.ByteOrder;
+import javax.inject.Inject;
+
public class VnetProtocol extends BaseProtocol {
- public VnetProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public VnetProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1500, 4, 2, 12, 0, true));
pipeline.addLast(new VnetProtocolDecoder(VnetProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/VnetProtocolDecoder.java b/src/main/java/org/traccar/protocol/VnetProtocolDecoder.java
index 1ee00bd3f..048c89e1e 100644
--- a/src/main/java/org/traccar/protocol/VnetProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/VnetProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BcdUtil;
diff --git a/src/main/java/org/traccar/protocol/Vt200Protocol.java b/src/main/java/org/traccar/protocol/Vt200Protocol.java
index 2a9ef6ab5..efb5fe2fd 100644
--- a/src/main/java/org/traccar/protocol/Vt200Protocol.java
+++ b/src/main/java/org/traccar/protocol/Vt200Protocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class Vt200Protocol extends BaseProtocol {
- public Vt200Protocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public Vt200Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new Vt200FrameDecoder());
pipeline.addLast(new Vt200ProtocolDecoder(Vt200Protocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/Vt200ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Vt200ProtocolDecoder.java
index 84ad09caa..a8fc801e7 100644
--- a/src/main/java/org/traccar/protocol/Vt200ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Vt200ProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BcdUtil;
import org.traccar.helper.BitUtil;
diff --git a/src/main/java/org/traccar/protocol/VtfmsProtocol.java b/src/main/java/org/traccar/protocol/VtfmsProtocol.java
index 2826a86e6..482ab4a37 100644
--- a/src/main/java/org/traccar/protocol/VtfmsProtocol.java
+++ b/src/main/java/org/traccar/protocol/VtfmsProtocol.java
@@ -15,26 +15,29 @@
*/
package org.traccar.protocol;
+import io.netty.handler.codec.string.StringDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
-import io.netty.handler.codec.string.StringDecoder;
+import javax.inject.Inject;
public class VtfmsProtocol extends BaseProtocol {
- public VtfmsProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public VtfmsProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new VtfmsFrameDecoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new VtfmsProtocolDecoder(VtfmsProtocol.this));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringDecoder());
pipeline.addLast(new VtfmsProtocolDecoder(VtfmsProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/VtfmsProtocolDecoder.java b/src/main/java/org/traccar/protocol/VtfmsProtocolDecoder.java
index 17fac4311..bf0cdcb51 100644
--- a/src/main/java/org/traccar/protocol/VtfmsProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/VtfmsProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/WatchProtocol.java b/src/main/java/org/traccar/protocol/WatchProtocol.java
index 6dc3bf9fb..600f81328 100644
--- a/src/main/java/org/traccar/protocol/WatchProtocol.java
+++ b/src/main/java/org/traccar/protocol/WatchProtocol.java
@@ -18,11 +18,15 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
+import javax.inject.Inject;
+
public class WatchProtocol extends BaseProtocol {
- public WatchProtocol() {
+ @Inject
+ public WatchProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_CUSTOM,
Command.TYPE_POSITION_SINGLE,
@@ -40,9 +44,9 @@ public class WatchProtocol extends BaseProtocol {
Command.TYPE_VOICE_MESSAGE,
Command.TYPE_SET_TIMEZONE,
Command.TYPE_SET_INDICATOR);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new WatchFrameDecoder());
pipeline.addLast(new WatchProtocolEncoder(WatchProtocol.this));
pipeline.addLast(new WatchProtocolDecoder(WatchProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java b/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java
index 4990cfd65..142d1b64f 100644
--- a/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,14 +18,12 @@ package org.traccar.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
+import org.traccar.helper.BufferUtil;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -41,7 +39,7 @@ import java.util.regex.Pattern;
public class WatchProtocolDecoder extends BaseProtocolDecoder {
- private static final Logger LOGGER = LoggerFactory.getLogger(WatchProtocolDecoder.class);
+ private ByteBuf audio;
public WatchProtocolDecoder(Protocol protocol) {
super(protocol);
@@ -89,6 +87,8 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
return Position.ALARM_GEOFENCE_EXIT;
} else if (BitUtil.check(status, 2)) {
return Position.ALARM_GEOFENCE_ENTER;
+ } else if (BitUtil.check(status, 14)) {
+ return Position.ALARM_POWER_CUT;
} else if (BitUtil.check(status, 16)) {
return Position.ALARM_SOS;
} else if (BitUtil.check(status, 17)) {
@@ -142,14 +142,21 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
Network network = new Network();
int cellCount = Integer.parseInt(values[index++]);
- index += 1; // timing advance
- int mcc = !values[index].isEmpty() ? Integer.parseInt(values[index++]) : 0;
- int mnc = !values[index].isEmpty() ? Integer.parseInt(values[index++]) : 0;
-
- for (int i = 0; i < cellCount; i++) {
- network.addCellTower(CellTower.from(mcc, mnc,
- Integer.parseInt(values[index++]), Integer.parseInt(values[index++]),
- Integer.parseInt(values[index++])));
+ if (cellCount > 0) {
+ index += 1; // timing advance
+ int mcc = !values[index].isEmpty() ? Integer.parseInt(values[index++]) : 0;
+ int mnc = !values[index].isEmpty() ? Integer.parseInt(values[index++]) : 0;
+
+ for (int i = 0; i < cellCount; i++) {
+ int lac = Integer.parseInt(values[index++]);
+ int cid = Integer.parseInt(values[index++]);
+ String rssi = values[index++];
+ if (!rssi.isEmpty()) {
+ network.addCellTower(CellTower.from(mcc, mnc, lac, cid, Integer.parseInt(rssi)));
+ } else {
+ network.addCellTower(CellTower.from(mcc, mnc, lac, cid));
+ }
+ }
}
if (index < values.length && !values[index].isEmpty()) {
@@ -157,8 +164,11 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
for (int i = 0; i < wifiCount; i++) {
index += 1; // wifi name
- network.addWifiAccessPoint(WifiAccessPoint.from(
- values[index++], Integer.parseInt(values[index++])));
+ String macAddress = values[index++];
+ String rssi = values[index++];
+ if (!macAddress.isEmpty() && !macAddress.equals("0") && !rssi.isEmpty()) {
+ network.addWifiAccessPoint(WifiAccessPoint.from(macAddress, Integer.parseInt(rssi)));
+ }
}
}
@@ -253,6 +263,9 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
Position position = decodePosition(deviceSession, buf.toString(StandardCharsets.US_ASCII));
if (type.startsWith("AL")) {
+ if (position != null) {
+ position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
+ }
sendResponse(channel, id, index, "AL");
}
@@ -305,17 +318,41 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
int timeIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) ',');
buf.readerIndex(timeIndex + 12 + 2);
- position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(id, buf, "jpg"));
+ position.set(Position.KEY_IMAGE, writeMediaFile(id, buf, "jpg"));
return position;
+ } else if (type.equals("JXTK")) {
+
+ int dataIndex = BufferUtil.indexOf(buf, buf.readerIndex(), buf.writerIndex(), (byte) ',', 4) + 1;
+ String[] values = buf.readCharSequence(
+ dataIndex - buf.readerIndex(), StandardCharsets.US_ASCII).toString().split(",");
+
+ int current = Integer.parseInt(values[2]);
+ int total = Integer.parseInt(values[3]);
+
+ if (audio == null) {
+ audio = Unpooled.buffer();
+ }
+ audio.writeBytes(buf);
+
+ sendResponse(channel, id, index, "JXTKR,1");
+
+ if (current < total) {
+ return null;
+ } else {
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ getLastLocation(position, null);
+ position.set(Position.KEY_AUDIO, writeMediaFile(id, audio, "amr"));
+ audio.release();
+ audio = null;
+ return position;
+ }
+
} else if (type.equals("TK")) {
if (buf.readableBytes() == 1) {
- byte result = buf.readByte();
- if (result != '1') {
- LOGGER.warn(type + "," + result);
- }
return null;
}
@@ -324,7 +361,7 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
getLastLocation(position, null);
- position.set(Position.KEY_AUDIO, Context.getMediaManager().writeFile(id, buf, "amr"));
+ position.set(Position.KEY_AUDIO, writeMediaFile(id, buf, "amr"));
return position;
diff --git a/src/main/java/org/traccar/protocol/WatchProtocolEncoder.java b/src/main/java/org/traccar/protocol/WatchProtocolEncoder.java
index f1904ea4d..14ebe2852 100644
--- a/src/main/java/org/traccar/protocol/WatchProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/WatchProtocolEncoder.java
@@ -67,6 +67,9 @@ public class WatchProtocolEncoder extends StringProtocolEncoder implements Strin
if (decoder != null) {
hasIndex = decoder.getHasIndex();
manufacturer = decoder.getManufacturer();
+ if (manufacturer.equals("3G")) {
+ manufacturer = "SG";
+ }
}
}
diff --git a/src/main/java/org/traccar/protocol/WialonProtocol.java b/src/main/java/org/traccar/protocol/WialonProtocol.java
index cb6ea5319..a744349cd 100644
--- a/src/main/java/org/traccar/protocol/WialonProtocol.java
+++ b/src/main/java/org/traccar/protocol/WialonProtocol.java
@@ -15,32 +15,34 @@
*/
package org.traccar.protocol;
+import io.netty.handler.codec.LineBasedFrameDecoder;
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
-import org.traccar.Context;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.model.Command;
-import io.netty.handler.codec.LineBasedFrameDecoder;
-import io.netty.handler.codec.string.StringDecoder;
-import io.netty.handler.codec.string.StringEncoder;
-
import java.nio.charset.StandardCharsets;
+import javax.inject.Inject;
+
public class WialonProtocol extends BaseProtocol {
- public WialonProtocol() {
+ @Inject
+ public WialonProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_REBOOT_DEVICE,
Command.TYPE_SEND_USSD,
Command.TYPE_IDENTIFICATION,
Command.TYPE_OUTPUT_CONTROL);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(4 * 1024));
- boolean utf8 = Context.getConfig().getBoolean(Keys.PROTOCOL_UTF8.withPrefix(getName()));
+ boolean utf8 = config.getBoolean(Keys.PROTOCOL_UTF8.withPrefix(getName()));
if (utf8) {
pipeline.addLast(new StringEncoder(StandardCharsets.UTF_8));
pipeline.addLast(new StringDecoder(StandardCharsets.UTF_8));
@@ -52,11 +54,11 @@ public class WialonProtocol extends BaseProtocol {
pipeline.addLast(new WialonProtocolDecoder(WialonProtocol.this));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(4 * 1024));
- boolean utf8 = Context.getConfig().getBoolean(Keys.PROTOCOL_UTF8.withPrefix(getName()));
+ boolean utf8 = config.getBoolean(Keys.PROTOCOL_UTF8.withPrefix(getName()));
if (utf8) {
pipeline.addLast(new StringEncoder(StandardCharsets.UTF_8));
pipeline.addLast(new StringDecoder(StandardCharsets.UTF_8));
diff --git a/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java b/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java
index 80299ff08..3d57525b7 100644
--- a/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
@@ -39,7 +39,7 @@ public class WialonProtocolDecoder extends BaseProtocolDecoder {
}
private static final Pattern PATTERN_ANY = new PatternBuilder()
- .expression("([^#]*)?") // imei
+ .expression("([^#]+)?") // imei
.text("#") // start byte
.expression("([^#]+)") // type
.text("#") // separator
@@ -47,13 +47,13 @@ public class WialonProtocolDecoder extends BaseProtocolDecoder {
.compile();
private static final Pattern PATTERN = new PatternBuilder()
- .number("(dd)(dd)(dd);") // date (ddmmyy)
- .number("(dd)(dd)(dd);") // time (hhmmss)
+ .number("(?:NA|(dd)(dd)(dd));") // date (ddmmyy)
+ .number("(?:NA|(dd)(dd)(dd));") // time (hhmmss)
.number("(?:NA|(dd)(dd.d+));") // latitude
.expression("(?:NA|([NS]));")
.number("(?:NA|(ddd)(dd.d+));") // longitude
.expression("(?:NA|([EW]));")
- .number("(d+.?d*)?;") // speed
+ .number("(?:NA|(d+.?d*))?;") // speed
.number("(?:NA|(d+.?d*))?;") // course
.number("(?:NA|(-?d+.?d*));") // altitude
.number("(?:NA|(d+))") // satellites
@@ -95,7 +95,11 @@ public class WialonProtocolDecoder extends BaseProtocolDecoder {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+ if (parser.hasNext(6)) {
+ position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+ } else {
+ position.setTime(new Date());
+ }
if (parser.hasNext(9)) {
position.setLatitude(parser.nextCoordinate());
diff --git a/src/main/java/org/traccar/protocol/WliProtocol.java b/src/main/java/org/traccar/protocol/WliProtocol.java
index c10ebf505..f7084e55b 100644
--- a/src/main/java/org/traccar/protocol/WliProtocol.java
+++ b/src/main/java/org/traccar/protocol/WliProtocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class WliProtocol extends BaseProtocol {
- public WliProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public WliProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new WliFrameDecoder());
pipeline.addLast(new WliProtocolDecoder(WliProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/WliProtocolDecoder.java b/src/main/java/org/traccar/protocol/WliProtocolDecoder.java
index 0e2a0a65e..ec1c4d17a 100644
--- a/src/main/java/org/traccar/protocol/WliProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/WliProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2020 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,10 +18,12 @@ package org.traccar.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.UnitsConverter;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
import org.traccar.model.Position;
import java.net.SocketAddress;
@@ -41,9 +43,9 @@ public class WliProtocolDecoder extends BaseProtocolDecoder {
ByteBuf buf = (ByteBuf) msg;
buf.readUnsignedByte(); // header
- int type = buf.readUnsignedByte();
+ int clazz = buf.readUnsignedByte();
- if (type == '1') {
+ if (clazz == '1') {
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
if (deviceSession == null) {
@@ -53,11 +55,13 @@ public class WliProtocolDecoder extends BaseProtocolDecoder {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
+ CellTower cellTower = new CellTower();
+
position.set(Position.KEY_INDEX, buf.readUnsignedShort());
buf.readUnsignedShort(); // length
buf.readUnsignedShort(); // checksum
- buf.readUnsignedByte(); // application message type
+ int type = buf.readUnsignedByte();
buf.readUnsignedByte(); // delimiter
while (buf.readableBytes() > 1) {
@@ -95,18 +99,56 @@ public class WliProtocolDecoder extends BaseProtocolDecoder {
String value = buf.readCharSequence(
endIndex - buf.readerIndex(), StandardCharsets.US_ASCII).toString();
- switch (fieldNumber) {
- case 246:
- String[] values = value.split(",");
- position.set(Position.KEY_POWER, Integer.parseInt(values[2]) * 0.01);
- position.set(Position.KEY_BATTERY, Integer.parseInt(values[3]) * 0.01);
+ int networkFieldsOffset;
+ switch (type) {
+ case 0xE4:
+ networkFieldsOffset = 10;
+ break;
+ case 0xCB:
+ networkFieldsOffset = 80;
break;
- case 255:
- position.setDeviceTime(new Date(Long.parseLong(value) * 1000));
+ case 0x1E:
+ networkFieldsOffset = 182;
break;
+ case 0xC9:
default:
+ networkFieldsOffset = 35;
break;
}
+ if (fieldNumber - networkFieldsOffset >= 0 && fieldNumber - networkFieldsOffset < 10) {
+ switch (fieldNumber - networkFieldsOffset) {
+ case 0:
+ cellTower.setMobileCountryCode(Integer.parseInt(value));
+ break;
+ case 1:
+ cellTower.setMobileNetworkCode(Integer.parseInt(value));
+ break;
+ case 2:
+ cellTower.setLocationAreaCode(Integer.parseInt(value));
+ break;
+ case 3:
+ cellTower.setCellId(Long.parseLong(value));
+ break;
+ case 4:
+ cellTower.setSignalStrength(Integer.parseInt(value));
+ break;
+ default:
+ break;
+ }
+ } else {
+ switch (fieldNumber) {
+ case 246:
+ String[] values = value.split(",");
+ position.set(Position.KEY_POWER, Integer.parseInt(values[2]) * 0.01);
+ position.set(Position.KEY_BATTERY, Integer.parseInt(values[3]) * 0.01);
+ break;
+ case 255:
+ position.setDeviceTime(new Date(Long.parseLong(value) * 1000));
+ break;
+ default:
+ break;
+ }
+ }
}
@@ -114,13 +156,21 @@ public class WliProtocolDecoder extends BaseProtocolDecoder {
}
+ if (type == 0xE4) {
+ getLastLocation(position, position.getDeviceTime());
+ }
+
+ if (cellTower.getCellId() != null) {
+ position.setNetwork(new Network(cellTower));
+ }
+
if (!position.getValid()) {
getLastLocation(position, position.getDeviceTime());
}
return position;
- } else if (type == '2') {
+ } else if (clazz == '2') {
String id = buf.toString(buf.readerIndex(), buf.readableBytes() - 1, StandardCharsets.US_ASCII);
getDeviceSession(channel, remoteAddress, id.substring("wli:".length()));
diff --git a/src/main/java/org/traccar/protocol/WondexProtocol.java b/src/main/java/org/traccar/protocol/WondexProtocol.java
index 6401fde85..5a0401df4 100644
--- a/src/main/java/org/traccar/protocol/WondexProtocol.java
+++ b/src/main/java/org/traccar/protocol/WondexProtocol.java
@@ -15,16 +15,19 @@
*/
package org.traccar.protocol;
+import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
-import io.netty.handler.codec.string.StringEncoder;
+import javax.inject.Inject;
public class WondexProtocol extends BaseProtocol {
- public WondexProtocol() {
+ @Inject
+ public WondexProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_GET_DEVICE_STATUS,
Command.TYPE_GET_MODEM_STATUS,
@@ -40,18 +43,18 @@ public class WondexProtocol extends BaseProtocol {
Command.TYPE_POSITION_SINGLE,
Command.TYPE_GET_VERSION,
Command.TYPE_IDENTIFICATION);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new WondexFrameDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new WondexProtocolEncoder(WondexProtocol.this));
pipeline.addLast(new WondexProtocolDecoder(WondexProtocol.this));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringEncoder());
pipeline.addLast(new WondexProtocolEncoder(WondexProtocol.this));
pipeline.addLast(new WondexProtocolDecoder(WondexProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/WondexProtocolDecoder.java b/src/main/java/org/traccar/protocol/WondexProtocolDecoder.java
index b85ae2656..46aa65a5d 100644
--- a/src/main/java/org/traccar/protocol/WondexProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/WondexProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,7 @@ package org.traccar.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -74,6 +74,9 @@ public class WondexProtocolDecoder extends BaseProtocolDecoder {
|| buf.toString(StandardCharsets.US_ASCII).startsWith("$MSG:")) {
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
@@ -89,12 +92,12 @@ public class WondexProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position(getProtocolName());
-
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
return null;
}
+
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.setTime(parser.nextDateTime());
diff --git a/src/main/java/org/traccar/protocol/WondexProtocolEncoder.java b/src/main/java/org/traccar/protocol/WondexProtocolEncoder.java
index 21f1ee321..fb213dc40 100644
--- a/src/main/java/org/traccar/protocol/WondexProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/WondexProtocolEncoder.java
@@ -5,7 +5,7 @@
* 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
+ * 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,
diff --git a/src/main/java/org/traccar/protocol/WristbandProtocol.java b/src/main/java/org/traccar/protocol/WristbandProtocol.java
index 1e5ef2c01..c5d8d4050 100644
--- a/src/main/java/org/traccar/protocol/WristbandProtocol.java
+++ b/src/main/java/org/traccar/protocol/WristbandProtocol.java
@@ -19,13 +19,17 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class WristbandProtocol extends BaseProtocol {
- public WristbandProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public WristbandProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 3, 2, 3, 0));
pipeline.addLast(new WristbandProtocolDecoder(WristbandProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/WristbandProtocolDecoder.java b/src/main/java/org/traccar/protocol/WristbandProtocolDecoder.java
index 58b5784d0..323992ddd 100644
--- a/src/main/java/org/traccar/protocol/WristbandProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/WristbandProtocolDecoder.java
@@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/Xexun2FrameEncoder.java b/src/main/java/org/traccar/protocol/Xexun2FrameEncoder.java
new file mode 100644
index 000000000..52d43c36c
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Xexun2FrameEncoder.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2022 Stefan Clark (stefan@stefanclark.co.uk)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.MessageToByteEncoder;
+
+public class Xexun2FrameEncoder extends MessageToByteEncoder<ByteBuf> {
+
+ @Override
+ protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) {
+ out.writeBytes(msg.readBytes(2));
+
+ while (msg.readableBytes() > 2) {
+ int b = msg.readUnsignedByte();
+ if (b == 0xfa && msg.isReadable() && msg.getUnsignedByte(msg.readerIndex()) == 0xaf) {
+ msg.readUnsignedByte();
+ out.writeByte(0xfb);
+ out.writeByte(0xbf);
+ out.writeByte(0x01);
+ } else if (b == 0xfb && msg.isReadable() && msg.getUnsignedByte(msg.readerIndex()) == 0xbf) {
+ msg.readUnsignedByte();
+ out.writeByte(0xfb);
+ out.writeByte(0xbf);
+ out.writeByte(0x02);
+ } else {
+ out.writeByte(b);
+ }
+ }
+
+ out.writeBytes(msg.readBytes(2));
+
+ }
+}
diff --git a/src/main/java/org/traccar/protocol/Xexun2Protocol.java b/src/main/java/org/traccar/protocol/Xexun2Protocol.java
index 265841c77..52cf731f0 100644
--- a/src/main/java/org/traccar/protocol/Xexun2Protocol.java
+++ b/src/main/java/org/traccar/protocol/Xexun2Protocol.java
@@ -18,15 +18,27 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+import org.traccar.model.Command;
+
+import javax.inject.Inject;
public class Xexun2Protocol extends BaseProtocol {
- public Xexun2Protocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public Xexun2Protocol(Config config) {
+ setSupportedDataCommands(
+ Command.TYPE_CUSTOM,
+ Command.TYPE_POSITION_PERIODIC,
+ Command.TYPE_POWER_OFF,
+ Command.TYPE_REBOOT_DEVICE);
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
+ pipeline.addLast(new Xexun2FrameEncoder());
pipeline.addLast(new Xexun2FrameDecoder());
pipeline.addLast(new Xexun2ProtocolDecoder(Xexun2Protocol.this));
+ pipeline.addLast(new Xexun2ProtocolEncoder(Xexun2Protocol.this));
}
});
}
diff --git a/src/main/java/org/traccar/protocol/Xexun2ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Xexun2ProtocolDecoder.java
index 766a3f05b..913dfaf28 100644
--- a/src/main/java/org/traccar/protocol/Xexun2ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Xexun2ProtocolDecoder.java
@@ -20,10 +20,11 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
+import org.traccar.helper.Checksum;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.CellTower;
import org.traccar.model.Network;
@@ -41,13 +42,14 @@ public class Xexun2ProtocolDecoder extends BaseProtocolDecoder {
super(protocol);
}
+ public static final int FLAG = 0xfaaf;
+ public static final int MSG_COMMAND = 0x07;
public static final int MSG_POSITION = 0x14;
private void sendResponse(Channel channel, int type, int index, ByteBuf imei) {
if (channel != null) {
ByteBuf response = Unpooled.buffer();
- response.writeByte(0xfa);
- response.writeByte(0xaf);
+ response.writeShort(FLAG);
response.writeShort(type);
response.writeShort(index);
@@ -56,8 +58,7 @@ public class Xexun2ProtocolDecoder extends BaseProtocolDecoder {
response.writeShort(0xfffe); // checksum
response.writeByte(1); // response
- response.writeByte(0xfa);
- response.writeByte(0xaf);
+ response.writeShort(FLAG);
channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
}
@@ -67,6 +68,9 @@ public class Xexun2ProtocolDecoder extends BaseProtocolDecoder {
if (BitUtil.check(value, 0)) {
return Position.ALARM_SOS;
}
+ if (BitUtil.check(value, 1)) {
+ return Position.ALARM_REMOVING;
+ }
if (BitUtil.check(value, 15)) {
return Position.ALARM_FALL_DOWN;
}
@@ -96,10 +100,16 @@ public class Xexun2ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- sendResponse(channel, type, index, imei);
+ int payloadSize = buf.readUnsignedShort() & 0x03ff;
+ int checksum = buf.readUnsignedShort();
+
+ if (checksum != Checksum.ip(buf.nioBuffer(buf.readerIndex(), payloadSize))) {
+ return null;
+ }
- buf.readUnsignedShort(); // attributes
- buf.readUnsignedShort(); // checksum
+ if (type != MSG_COMMAND) {
+ sendResponse(channel, type, index, imei);
+ }
if (type == MSG_POSITION) {
List<Integer> lengths = new ArrayList<>();
@@ -173,7 +183,25 @@ public class Xexun2ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
position.setLongitude(convertCoordinate(buf.readDouble()));
position.setLatitude(convertCoordinate(buf.readDouble()));
-
+ }
+ if (BitUtil.check(positionMask, 7)) {
+ int dataLength = buf.readUnsignedShort();
+ if (dataLength > 0) {
+ int dataType = buf.readUnsignedByte();
+ int dataEndIndex = buf.readerIndex() + buf.readUnsignedShort();
+ if (dataType == 'G') {
+ position.setFixTime(position.getDeviceTime());
+ position.setLongitude(convertCoordinate(buf.readDouble()));
+ position.setLatitude(convertCoordinate(buf.readDouble()));
+ position.setValid(buf.readUnsignedByte() > 0);
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ buf.readUnsignedByte(); // satellite signal-to-noise ratio
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort() * 0.1));
+ position.setCourse(buf.readUnsignedShort() * 0.1);
+ position.setAltitude(buf.readFloat());
+ }
+ buf.readerIndex(dataEndIndex);
+ }
}
}
if (BitUtil.check(mask, 3)) {
diff --git a/src/main/java/org/traccar/protocol/Xexun2ProtocolEncoder.java b/src/main/java/org/traccar/protocol/Xexun2ProtocolEncoder.java
new file mode 100644
index 000000000..8f3fa5672
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Xexun2ProtocolEncoder.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2022 Stefan Clark (stefan@stefanclark.co.uk)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import org.traccar.BaseProtocolEncoder;
+import org.traccar.helper.Checksum;
+import org.traccar.helper.DataConverter;
+import org.traccar.model.Command;
+import org.traccar.Protocol;
+
+import java.nio.charset.StandardCharsets;
+
+public class Xexun2ProtocolEncoder extends BaseProtocolEncoder {
+
+ public Xexun2ProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static ByteBuf encodeContent(String uniqueId, String content) {
+ ByteBuf buf = Unpooled.buffer();
+
+ ByteBuf message = Unpooled.copiedBuffer(content.getBytes(StandardCharsets.US_ASCII));
+
+ buf.writeShort(Xexun2ProtocolDecoder.FLAG);
+ buf.writeShort(Xexun2ProtocolDecoder.MSG_COMMAND);
+ buf.writeShort(1); // index
+ buf.writeBytes(DataConverter.parseHex(uniqueId + "0"));
+ buf.writeShort(message.readableBytes());
+ buf.writeShort(Checksum.ip(message.nioBuffer()));
+ buf.writeBytes(message);
+ buf.writeShort(Xexun2ProtocolDecoder.FLAG);
+
+ return buf;
+ }
+
+ @Override
+ protected Object encodeCommand(Command command) {
+ String uniqueId = getUniqueId(command.getDeviceId());
+
+ switch (command.getType()) {
+ case Command.TYPE_CUSTOM:
+ return encodeContent(uniqueId, command.getString(Command.KEY_DATA));
+ case Command.TYPE_POSITION_PERIODIC:
+ return encodeContent(uniqueId,
+ String.format("tracking_send=%1$d,%1$d", command.getInteger(Command.KEY_FREQUENCY)));
+ case Command.TYPE_POWER_OFF:
+ return encodeContent(uniqueId, "of=1");
+ case Command.TYPE_REBOOT_DEVICE:
+ return encodeContent(uniqueId, "reset");
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/XexunProtocol.java b/src/main/java/org/traccar/protocol/XexunProtocol.java
index b83c4e445..5c7329603 100644
--- a/src/main/java/org/traccar/protocol/XexunProtocol.java
+++ b/src/main/java/org/traccar/protocol/XexunProtocol.java
@@ -15,27 +15,29 @@
*/
package org.traccar.protocol;
+import io.netty.handler.codec.LineBasedFrameDecoder;
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
-import org.traccar.Context;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.model.Command;
-import io.netty.handler.codec.LineBasedFrameDecoder;
-import io.netty.handler.codec.string.StringDecoder;
-import io.netty.handler.codec.string.StringEncoder;
+import javax.inject.Inject;
public class XexunProtocol extends BaseProtocol {
- public XexunProtocol() {
+ @Inject
+ public XexunProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_ENGINE_STOP,
Command.TYPE_ENGINE_RESUME);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
- boolean full = Context.getConfig().getBoolean(Keys.PROTOCOL_EXTENDED.withPrefix(getName()));
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
+ boolean full = config.getBoolean(Keys.PROTOCOL_EXTENDED.withPrefix(getName()));
if (full) {
pipeline.addLast(new LineBasedFrameDecoder(1024)); // tracker bug \n\r
} else {
diff --git a/src/main/java/org/traccar/protocol/XexunProtocolDecoder.java b/src/main/java/org/traccar/protocol/XexunProtocolDecoder.java
index 73d386477..e41d467d5 100644
--- a/src/main/java/org/traccar/protocol/XexunProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/XexunProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/protocol/XirgoProtocol.java b/src/main/java/org/traccar/protocol/XirgoProtocol.java
index 1be5b6c4b..0841d86d5 100644
--- a/src/main/java/org/traccar/protocol/XirgoProtocol.java
+++ b/src/main/java/org/traccar/protocol/XirgoProtocol.java
@@ -15,23 +15,26 @@
*/
package org.traccar.protocol;
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
-import io.netty.handler.codec.string.StringDecoder;
-import io.netty.handler.codec.string.StringEncoder;
+import javax.inject.Inject;
public class XirgoProtocol extends BaseProtocol {
- public XirgoProtocol() {
+ @Inject
+ public XirgoProtocol(Config config) {
setSupportedDataCommands(
Command.TYPE_OUTPUT_CONTROL);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "##"));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
@@ -39,9 +42,9 @@ public class XirgoProtocol extends BaseProtocol {
pipeline.addLast(new XirgoProtocolDecoder(XirgoProtocol.this));
}
});
- addServer(new TrackerServer(true, getName()) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new XirgoProtocolEncoder(XirgoProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/XirgoProtocolDecoder.java b/src/main/java/org/traccar/protocol/XirgoProtocolDecoder.java
index 630fe5aed..220c28054 100644
--- a/src/main/java/org/traccar/protocol/XirgoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/XirgoProtocolDecoder.java
@@ -18,8 +18,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import io.netty.channel.socket.nio.NioDatagramChannel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.config.Keys;
@@ -40,7 +39,11 @@ public class XirgoProtocolDecoder extends BaseProtocolDecoder {
public XirgoProtocolDecoder(Protocol protocol) {
super(protocol);
- form = Context.getConfig().getString(Keys.PROTOCOL_FORM.withPrefix(getProtocolName()));
+ }
+
+ @Override
+ protected void init() {
+ form = getConfig().getString(Keys.PROTOCOL_FORM.withPrefix(getProtocolName()));
}
public void setForm(String form) {
diff --git a/src/main/java/org/traccar/protocol/Xrb28Protocol.java b/src/main/java/org/traccar/protocol/Xrb28Protocol.java
index 5d8af418b..65c2a1230 100644
--- a/src/main/java/org/traccar/protocol/Xrb28Protocol.java
+++ b/src/main/java/org/traccar/protocol/Xrb28Protocol.java
@@ -21,22 +21,26 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
import org.traccar.model.Command;
import java.nio.charset.StandardCharsets;
+import javax.inject.Inject;
+
public class Xrb28Protocol extends BaseProtocol {
- public Xrb28Protocol() {
+ @Inject
+ public Xrb28Protocol(Config config) {
setSupportedDataCommands(
Command.TYPE_CUSTOM,
Command.TYPE_POSITION_SINGLE,
Command.TYPE_POSITION_PERIODIC,
Command.TYPE_ALARM_ARM,
Command.TYPE_ALARM_DISARM);
- addServer(new TrackerServer(false, getName()) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringEncoder(StandardCharsets.ISO_8859_1));
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java
index 69e5b7372..88f2d07e5 100644
--- a/src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
diff --git a/src/main/java/org/traccar/protocol/Xt013Protocol.java b/src/main/java/org/traccar/protocol/Xt013Protocol.java
index ebb3c123f..9e9087609 100644
--- a/src/main/java/org/traccar/protocol/Xt013Protocol.java
+++ b/src/main/java/org/traccar/protocol/Xt013Protocol.java
@@ -15,20 +15,23 @@
*/
package org.traccar.protocol;
+import io.netty.handler.codec.LineBasedFrameDecoder;
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
-import io.netty.handler.codec.LineBasedFrameDecoder;
-import io.netty.handler.codec.string.StringDecoder;
-import io.netty.handler.codec.string.StringEncoder;
+import javax.inject.Inject;
public class Xt013Protocol extends BaseProtocol {
- public Xt013Protocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public Xt013Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
diff --git a/src/main/java/org/traccar/protocol/Xt013ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Xt013ProtocolDecoder.java
index f49fb9563..ab0b2cdaa 100644
--- a/src/main/java/org/traccar/protocol/Xt013ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Xt013ProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
diff --git a/src/main/java/org/traccar/protocol/Xt2400Protocol.java b/src/main/java/org/traccar/protocol/Xt2400Protocol.java
index 9427876c8..e200adb9f 100644
--- a/src/main/java/org/traccar/protocol/Xt2400Protocol.java
+++ b/src/main/java/org/traccar/protocol/Xt2400Protocol.java
@@ -18,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class Xt2400Protocol extends BaseProtocol {
- public Xt2400Protocol() {
- addServer(new TrackerServer(true, getName()) {
+ @Inject
+ public Xt2400Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), true) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new Xt2400ProtocolDecoder(Xt2400Protocol.this));
}
});
diff --git a/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java
index 85e8e140f..edcb3f535 100644
--- a/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java
@@ -18,8 +18,7 @@ package org.traccar.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.Protocol;
import org.traccar.config.Keys;
import org.traccar.helper.DataConverter;
@@ -38,8 +37,11 @@ public class Xt2400ProtocolDecoder extends BaseProtocolDecoder {
public Xt2400ProtocolDecoder(Protocol protocol) {
super(protocol);
+ }
- String config = Context.getConfig().getString(Keys.PROTOCOL_CONFIG.withPrefix(getProtocolName()));
+ @Override
+ protected void init() {
+ String config = getConfig().getString(Keys.PROTOCOL_CONFIG.withPrefix(getProtocolName()));
if (config != null) {
setConfig(config);
}
diff --git a/src/main/java/org/traccar/protocol/YwtProtocol.java b/src/main/java/org/traccar/protocol/YwtProtocol.java
index c525b75cf..fb44e2360 100644
--- a/src/main/java/org/traccar/protocol/YwtProtocol.java
+++ b/src/main/java/org/traccar/protocol/YwtProtocol.java
@@ -21,13 +21,17 @@ import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
public class YwtProtocol extends BaseProtocol {
- public YwtProtocol() {
- addServer(new TrackerServer(false, getName()) {
+ @Inject
+ public YwtProtocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
@Override
- protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
diff --git a/src/main/java/org/traccar/protocol/YwtProtocolDecoder.java b/src/main/java/org/traccar/protocol/YwtProtocolDecoder.java
index bf5a23fa7..fd050bee9 100644
--- a/src/main/java/org/traccar/protocol/YwtProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/YwtProtocolDecoder.java
@@ -17,7 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
+import org.traccar.session.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
diff --git a/src/main/java/org/traccar/reports/CsvExportProvider.java b/src/main/java/org/traccar/reports/CsvExportProvider.java
new file mode 100644
index 000000000..df55c470e
--- /dev/null
+++ b/src/main/java/org/traccar/reports/CsvExportProvider.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.reports;
+
+import org.traccar.helper.DateUtil;
+import org.traccar.helper.model.PositionUtil;
+import org.traccar.model.Position;
+import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
+
+import javax.inject.Inject;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+public class CsvExportProvider {
+
+ private final Storage storage;
+
+ @Inject
+ public CsvExportProvider(Storage storage) {
+ this.storage = storage;
+ }
+
+ public void generate(
+ OutputStream outputStream, long deviceId, Date from, Date to) throws StorageException {
+
+ var positions = PositionUtil.getPositions(storage, deviceId, from, to);
+
+ var attributes = positions.stream()
+ .flatMap((position -> position.getAttributes().keySet().stream()))
+ .collect(Collectors.toUnmodifiableSet());
+
+ var properties = new LinkedHashMap<String, Function<Position, Object>>();
+ properties.put("id", Position::getId);
+ properties.put("deviceId", Position::getDeviceId);
+ properties.put("protocol", Position::getProtocol);
+ properties.put("serverTime", position -> DateUtil.formatDate(position.getServerTime()));
+ properties.put("deviceTime", position -> DateUtil.formatDate(position.getDeviceTime()));
+ properties.put("fixTime", position -> DateUtil.formatDate(position.getFixTime()));
+ properties.put("valid", Position::getValid);
+ properties.put("latitude", Position::getLatitude);
+ properties.put("longitude", Position::getLongitude);
+ properties.put("altitude", Position::getAltitude);
+ properties.put("speed", Position::getSpeed);
+ properties.put("course", Position::getCourse);
+ properties.put("address", Position::getAddress);
+ properties.put("accuracy", Position::getAccuracy);
+ attributes.forEach(key -> properties.put(key, position -> position.getAttributes().get(key)));
+
+ try (PrintWriter writer = new PrintWriter(outputStream)) {
+ writer.println(String.join(",", properties.keySet()));
+ positions.forEach(position -> writer.println(properties.values().stream()
+ .map(f -> Objects.toString(f.apply(position), ""))
+ .collect(Collectors.joining(","))));
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/reports/Events.java b/src/main/java/org/traccar/reports/Events.java
deleted file mode 100644
index 66d9e708d..000000000
--- a/src/main/java/org/traccar/reports/Events.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
- * Copyright 2016 - 2018 Andrey Kunitsyn (andrey@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.reports;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Iterator;
-
-import org.apache.poi.ss.util.WorkbookUtil;
-import org.traccar.Context;
-import org.traccar.model.Device;
-import org.traccar.model.Event;
-import org.traccar.model.Geofence;
-import org.traccar.model.Group;
-import org.traccar.model.Maintenance;
-import org.traccar.reports.model.DeviceReport;
-
-public final class Events {
-
- private Events() {
- }
-
- public static Collection<Event> getObjects(long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
- Collection<String> types, Date from, Date to) throws SQLException {
- ReportUtils.checkPeriodLimit(from, to);
- ArrayList<Event> result = new ArrayList<>();
- for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
- Context.getPermissionsManager().checkDevice(userId, deviceId);
- Collection<Event> events = Context.getDataManager().getEvents(deviceId, from, to);
- boolean all = types.isEmpty() || types.contains(Event.ALL_EVENTS);
- for (Event event : events) {
- if (all || types.contains(event.getType())) {
- long geofenceId = event.getGeofenceId();
- long maintenanceId = event.getMaintenanceId();
- if ((geofenceId == 0 || Context.getGeofenceManager().checkItemPermission(userId, geofenceId))
- && (maintenanceId == 0
- || Context.getMaintenancesManager().checkItemPermission(userId, maintenanceId))) {
- result.add(event);
- }
- }
- }
- }
- return result;
- }
-
- public static void getExcel(OutputStream outputStream,
- long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
- Collection<String> types, Date from, Date to) throws SQLException, IOException {
- ReportUtils.checkPeriodLimit(from, to);
- ArrayList<DeviceReport> devicesEvents = new ArrayList<>();
- ArrayList<String> sheetNames = new ArrayList<>();
- HashMap<Long, String> geofenceNames = new HashMap<>();
- HashMap<Long, String> maintenanceNames = new HashMap<>();
- for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
- Context.getPermissionsManager().checkDevice(userId, deviceId);
- Collection<Event> events = Context.getDataManager().getEvents(deviceId, from, to);
- boolean all = types.isEmpty() || types.contains(Event.ALL_EVENTS);
- for (Iterator<Event> iterator = events.iterator(); iterator.hasNext();) {
- Event event = iterator.next();
- if (all || types.contains(event.getType())) {
- long geofenceId = event.getGeofenceId();
- long maintenanceId = event.getMaintenanceId();
- if (geofenceId != 0) {
- if (Context.getGeofenceManager().checkItemPermission(userId, geofenceId)) {
- Geofence geofence = Context.getGeofenceManager().getById(geofenceId);
- if (geofence != null) {
- geofenceNames.put(geofenceId, geofence.getName());
- }
- } else {
- iterator.remove();
- }
- } else if (maintenanceId != 0) {
- if (Context.getMaintenancesManager().checkItemPermission(userId, maintenanceId)) {
- Maintenance maintenance = Context.getMaintenancesManager().getById(maintenanceId);
- if (maintenance != null) {
- maintenanceNames.put(maintenanceId, maintenance.getName());
- }
- } else {
- iterator.remove();
- }
- }
- } else {
- iterator.remove();
- }
- }
- DeviceReport deviceEvents = new DeviceReport();
- Device device = Context.getIdentityManager().getById(deviceId);
- deviceEvents.setDeviceName(device.getName());
- sheetNames.add(WorkbookUtil.createSafeSheetName(deviceEvents.getDeviceName()));
- if (device.getGroupId() != 0) {
- Group group = Context.getGroupsManager().getById(device.getGroupId());
- if (group != null) {
- deviceEvents.setGroupName(group.getName());
- }
- }
- deviceEvents.setObjects(events);
- devicesEvents.add(deviceEvents);
- }
- String templatePath = Context.getConfig().getString("report.templatesPath",
- "templates/export/");
- try (InputStream inputStream = new FileInputStream(templatePath + "/events.xlsx")) {
- org.jxls.common.Context jxlsContext = ReportUtils.initializeContext(userId);
- jxlsContext.putVar("devices", devicesEvents);
- jxlsContext.putVar("sheetNames", sheetNames);
- jxlsContext.putVar("geofenceNames", geofenceNames);
- jxlsContext.putVar("maintenanceNames", maintenanceNames);
- jxlsContext.putVar("from", from);
- jxlsContext.putVar("to", to);
- ReportUtils.processTemplateWithSheets(inputStream, outputStream, jxlsContext);
- }
- }
-}
diff --git a/src/main/java/org/traccar/reports/EventsReportProvider.java b/src/main/java/org/traccar/reports/EventsReportProvider.java
new file mode 100644
index 000000000..30f55ba80
--- /dev/null
+++ b/src/main/java/org/traccar/reports/EventsReportProvider.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2018 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.reports;
+
+import org.apache.poi.ss.util.WorkbookUtil;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.model.Device;
+import org.traccar.model.Event;
+import org.traccar.model.Geofence;
+import org.traccar.model.Group;
+import org.traccar.model.Maintenance;
+import org.traccar.model.Position;
+import org.traccar.reports.common.ReportUtils;
+import org.traccar.reports.model.DeviceReportSection;
+import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Order;
+import org.traccar.storage.query.Request;
+
+import javax.inject.Inject;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+public class EventsReportProvider {
+
+ private final Config config;
+ private final ReportUtils reportUtils;
+ private final Storage storage;
+
+ @Inject
+ public EventsReportProvider(Config config, ReportUtils reportUtils, Storage storage) {
+ this.config = config;
+ this.reportUtils = reportUtils;
+ this.storage = storage;
+ }
+
+ private List<Event> getEvents(long deviceId, Date from, Date to) throws StorageException {
+ return storage.getObjects(Event.class, new Request(
+ new Columns.All(),
+ new Condition.And(
+ new Condition.Equals("deviceId", deviceId),
+ new Condition.Between("eventTime", "from", from, "to", to)),
+ new Order("eventTime")));
+ }
+
+ public Collection<Event> getObjects(
+ long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
+ Collection<String> types, Date from, Date to) throws StorageException {
+ reportUtils.checkPeriodLimit(from, to);
+
+ ArrayList<Event> result = new ArrayList<>();
+ for (Device device: reportUtils.getAccessibleDevices(userId, deviceIds, groupIds)) {
+ Collection<Event> events = getEvents(device.getId(), from, to);
+ boolean all = types.isEmpty() || types.contains(Event.ALL_EVENTS);
+ for (Event event : events) {
+ if (all || types.contains(event.getType())) {
+ long geofenceId = event.getGeofenceId();
+ long maintenanceId = event.getMaintenanceId();
+ if ((geofenceId == 0 || reportUtils.getObject(userId, Geofence.class, geofenceId) != null)
+ && (maintenanceId == 0
+ || reportUtils.getObject(userId, Maintenance.class, maintenanceId) != null)) {
+ result.add(event);
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ public void getExcel(
+ OutputStream outputStream, long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
+ Collection<String> types, Date from, Date to) throws StorageException, IOException {
+ reportUtils.checkPeriodLimit(from, to);
+
+ ArrayList<DeviceReportSection> devicesEvents = new ArrayList<>();
+ ArrayList<String> sheetNames = new ArrayList<>();
+ HashMap<Long, String> geofenceNames = new HashMap<>();
+ HashMap<Long, String> maintenanceNames = new HashMap<>();
+ HashMap<Long, Position> positions = new HashMap<>();
+ for (Device device: reportUtils.getAccessibleDevices(userId, deviceIds, groupIds)) {
+ Collection<Event> events = getEvents(device.getId(), from, to);
+ boolean all = types.isEmpty() || types.contains(Event.ALL_EVENTS);
+ for (Iterator<Event> iterator = events.iterator(); iterator.hasNext();) {
+ Event event = iterator.next();
+ if (all || types.contains(event.getType())) {
+ long geofenceId = event.getGeofenceId();
+ long maintenanceId = event.getMaintenanceId();
+ if (geofenceId != 0) {
+ Geofence geofence = reportUtils.getObject(userId, Geofence.class, geofenceId);
+ if (geofence != null) {
+ geofenceNames.put(geofenceId, geofence.getName());
+ } else {
+ iterator.remove();
+ }
+ } else if (maintenanceId != 0) {
+ Maintenance maintenance = reportUtils.getObject(userId, Maintenance.class, maintenanceId);
+ if (maintenance != null) {
+ maintenanceNames.put(maintenanceId, maintenance.getName());
+ } else {
+ iterator.remove();
+ }
+ }
+ } else {
+ iterator.remove();
+ }
+ }
+ for (Event event : events) {
+ long positionId = event.getPositionId();
+ if (positionId > 0) {
+ Position position = storage.getObject(Position.class, new Request(
+ new Columns.All(), new Condition.Equals("id", positionId)));
+ positions.put(positionId, position);
+ }
+ }
+ DeviceReportSection deviceEvents = new DeviceReportSection();
+ deviceEvents.setDeviceName(device.getName());
+ sheetNames.add(WorkbookUtil.createSafeSheetName(deviceEvents.getDeviceName()));
+ if (device.getGroupId() > 0) {
+ Group group = storage.getObject(Group.class, new Request(
+ new Columns.All(), new Condition.Equals("id", device.getGroupId())));
+ if (group != null) {
+ deviceEvents.setGroupName(group.getName());
+ }
+ }
+ deviceEvents.setObjects(events);
+ devicesEvents.add(deviceEvents);
+ }
+
+ File file = Paths.get(config.getString(Keys.TEMPLATES_ROOT), "export", "events.xlsx").toFile();
+ try (InputStream inputStream = new FileInputStream(file)) {
+ var context = reportUtils.initializeContext(userId);
+ context.putVar("devices", devicesEvents);
+ context.putVar("sheetNames", sheetNames);
+ context.putVar("geofenceNames", geofenceNames);
+ context.putVar("maintenanceNames", maintenanceNames);
+ context.putVar("positions", positions);
+ context.putVar("from", from);
+ context.putVar("to", to);
+ reportUtils.processTemplateWithSheets(inputStream, outputStream, context);
+ }
+ }
+}
diff --git a/src/main/java/org/traccar/reports/GpxExportProvider.java b/src/main/java/org/traccar/reports/GpxExportProvider.java
new file mode 100644
index 000000000..ccbd97fc3
--- /dev/null
+++ b/src/main/java/org/traccar/reports/GpxExportProvider.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.reports;
+
+import org.traccar.helper.DateUtil;
+import org.traccar.helper.model.PositionUtil;
+import org.traccar.model.Device;
+import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Request;
+
+import javax.inject.Inject;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.Date;
+
+public class GpxExportProvider {
+
+ private final Storage storage;
+
+ @Inject
+ public GpxExportProvider(Storage storage) {
+ this.storage = storage;
+ }
+
+ public void generate(
+ OutputStream outputStream, long deviceId, Date from, Date to) throws StorageException {
+
+ var device = storage.getObject(Device.class, new Request(
+ new Columns.All(), new Condition.Equals("id", deviceId)));
+ var positions = PositionUtil.getPositions(storage, deviceId, from, to);
+
+ try (PrintWriter writer = new PrintWriter(outputStream)) {
+ writer.print("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+ writer.print("<gpx version=\"1.0\">");
+ writer.print("<trk>");
+ writer.print("<name>");
+ writer.print(device.getName());
+ writer.print("</name>");
+ writer.print("<trkseg>");
+ positions.forEach(position -> {
+ writer.print("<trkpt lat=\"");
+ writer.print(position.getLatitude());
+ writer.print("\" lon=\"");
+ writer.print(position.getLongitude());
+ writer.print("\">");
+ writer.print("<ele>");
+ writer.print(position.getAltitude());
+ writer.print("</ele>");
+ writer.print("<time>");
+ writer.print(DateUtil.formatDate(position.getFixTime()));
+ writer.print("</time>");
+ writer.print("</trkpt>");
+ });
+ writer.print("</trkseg>");
+ writer.print("</trk>");
+ writer.print("</gpx>");
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/reports/KmlExportProvider.java b/src/main/java/org/traccar/reports/KmlExportProvider.java
new file mode 100644
index 000000000..24fcfb8ab
--- /dev/null
+++ b/src/main/java/org/traccar/reports/KmlExportProvider.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.reports;
+
+import org.traccar.helper.model.PositionUtil;
+import org.traccar.model.Device;
+import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Request;
+
+import javax.inject.Inject;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.stream.Collectors;
+
+public class KmlExportProvider {
+
+ private final Storage storage;
+
+ @Inject
+ public KmlExportProvider(Storage storage) {
+ this.storage = storage;
+ }
+
+ public void generate(
+ OutputStream outputStream, long deviceId, Date from, Date to) throws StorageException {
+
+ var device = storage.getObject(Device.class, new Request(
+ new Columns.All(), new Condition.Equals("id", deviceId)));
+ var positions = PositionUtil.getPositions(storage, deviceId, from, to);
+
+ var dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
+
+ try (PrintWriter writer = new PrintWriter(outputStream)) {
+ writer.print("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+ writer.print("<kml xmlns=\"http://www.opengis.net/kml/2.2\">");
+ writer.print("<Document>");
+ writer.print("<name>");
+ writer.print(device.getName());
+ writer.print("</name>");
+ writer.print("<Placemark>");
+ writer.print("<name>");
+ writer.print(dateFormat.format(from));
+ writer.print(" - ");
+ writer.print(dateFormat.format(to));
+ writer.print("</name>");
+ writer.print("<LineString>");
+ writer.print("<extrude>1</extrude>");
+ writer.print("<tessellate>1</tessellate>");
+ writer.print("<altitudeMode>absolute</altitudeMode>");
+ writer.print("<coordinates>");
+ writer.print(positions.stream()
+ .map((p -> String.format("%f,%f,%f", p.getLongitude(), p.getLatitude(), p.getAltitude())))
+ .collect(Collectors.joining(" ")));
+ writer.print("</coordinates>");
+ writer.print("</LineString>");
+ writer.print("</Placemark>");
+ writer.print("</Document>");
+ writer.print("</kml>");
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/reports/ReportUtils.java b/src/main/java/org/traccar/reports/ReportUtils.java
deleted file mode 100644
index 58674beae..000000000
--- a/src/main/java/org/traccar/reports/ReportUtils.java
+++ /dev/null
@@ -1,368 +0,0 @@
-/*
- * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org)
- * Copyright 2016 - 2017 Andrey Kunitsyn (andrey@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.reports;
-
-import org.apache.velocity.tools.generic.DateTool;
-import org.apache.velocity.tools.generic.NumberTool;
-import org.jxls.area.Area;
-import org.jxls.builder.xls.XlsCommentAreaBuilder;
-import org.jxls.common.CellRef;
-import org.jxls.formula.StandardFormulaProcessor;
-import org.jxls.transform.Transformer;
-import org.jxls.transform.poi.PoiTransformer;
-import org.jxls.util.TransformerFactory;
-import org.traccar.Context;
-import org.traccar.config.Keys;
-import org.traccar.database.DeviceManager;
-import org.traccar.database.IdentityManager;
-import org.traccar.handler.events.MotionEventHandler;
-import org.traccar.helper.UnitsConverter;
-import org.traccar.model.DeviceState;
-import org.traccar.model.Driver;
-import org.traccar.model.Event;
-import org.traccar.model.Position;
-import org.traccar.reports.model.BaseReport;
-import org.traccar.reports.model.StopReport;
-import org.traccar.reports.model.TripReport;
-import org.traccar.reports.model.TripsConfig;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Date;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.TimeZone;
-
-public final class ReportUtils {
-
- private ReportUtils() {
- }
-
- public static void checkPeriodLimit(Date from, Date to) {
- long limit = Context.getConfig().getLong(Keys.REPORT_PERIOD_LIMIT) * 1000;
- if (limit > 0 && to.getTime() - from.getTime() > limit) {
- throw new IllegalArgumentException("Time period exceeds the limit");
- }
- }
-
- public static String getDistanceUnit(long userId) {
- return (String) Context.getPermissionsManager().lookupAttribute(userId, "distanceUnit", "km");
- }
-
- public static String getSpeedUnit(long userId) {
- return (String) Context.getPermissionsManager().lookupAttribute(userId, "speedUnit", "kn");
- }
-
- public static String getVolumeUnit(long userId) {
- return (String) Context.getPermissionsManager().lookupAttribute(userId, "volumeUnit", "ltr");
- }
-
- public static TimeZone getTimezone(long userId) {
- String timezone = (String) Context.getPermissionsManager().lookupAttribute(userId, "timezone", null);
- return timezone != null ? TimeZone.getTimeZone(timezone) : TimeZone.getDefault();
- }
-
- public static Collection<Long> getDeviceList(Collection<Long> deviceIds, Collection<Long> groupIds) {
- Collection<Long> result = new ArrayList<>(deviceIds);
- for (long groupId : groupIds) {
- result.addAll(Context.getPermissionsManager().getGroupDevices(groupId));
- }
- return result;
- }
-
- public static double calculateDistance(Position firstPosition, Position lastPosition) {
- return calculateDistance(firstPosition, lastPosition, true);
- }
-
- public static double calculateDistance(Position firstPosition, Position lastPosition, boolean useOdometer) {
- double distance = 0.0;
- double firstOdometer = firstPosition.getDouble(Position.KEY_ODOMETER);
- double lastOdometer = lastPosition.getDouble(Position.KEY_ODOMETER);
-
- if (useOdometer && firstOdometer != 0.0 && lastOdometer != 0.0) {
- distance = lastOdometer - firstOdometer;
- } else if (firstPosition.getAttributes().containsKey(Position.KEY_TOTAL_DISTANCE)
- && lastPosition.getAttributes().containsKey(Position.KEY_TOTAL_DISTANCE)) {
- distance = lastPosition.getDouble(Position.KEY_TOTAL_DISTANCE)
- - firstPosition.getDouble(Position.KEY_TOTAL_DISTANCE);
- }
- return distance;
- }
-
- public static double calculateFuel(Position firstPosition, Position lastPosition) {
-
- if (firstPosition.getAttributes().get(Position.KEY_FUEL_LEVEL) != null
- && lastPosition.getAttributes().get(Position.KEY_FUEL_LEVEL) != null) {
-
- BigDecimal value = BigDecimal.valueOf(firstPosition.getDouble(Position.KEY_FUEL_LEVEL)
- - lastPosition.getDouble(Position.KEY_FUEL_LEVEL));
- return value.setScale(1, RoundingMode.HALF_EVEN).doubleValue();
- }
- return 0;
- }
-
- public static String findDriver(Position firstPosition, Position lastPosition) {
- if (firstPosition.getAttributes().containsKey(Position.KEY_DRIVER_UNIQUE_ID)) {
- return firstPosition.getString(Position.KEY_DRIVER_UNIQUE_ID);
- } else if (lastPosition.getAttributes().containsKey(Position.KEY_DRIVER_UNIQUE_ID)) {
- return lastPosition.getString(Position.KEY_DRIVER_UNIQUE_ID);
- }
- return null;
- }
-
- public static String findDriverName(String driverUniqueId) {
- if (driverUniqueId != null && Context.getDriversManager() != null) {
- Driver driver = Context.getDriversManager().getDriverByUniqueId(driverUniqueId);
- if (driver != null) {
- return driver.getName();
- }
- }
- return null;
- }
-
- public static org.jxls.common.Context initializeContext(long userId) {
- org.jxls.common.Context jxlsContext = PoiTransformer.createInitialContext();
- jxlsContext.putVar("distanceUnit", getDistanceUnit(userId));
- jxlsContext.putVar("speedUnit", getSpeedUnit(userId));
- jxlsContext.putVar("volumeUnit", getVolumeUnit(userId));
- jxlsContext.putVar("webUrl", Context.getVelocityEngine().getProperty("web.url"));
- jxlsContext.putVar("dateTool", new DateTool());
- jxlsContext.putVar("numberTool", new NumberTool());
- jxlsContext.putVar("timezone", getTimezone(userId));
- jxlsContext.putVar("locale", Locale.getDefault());
- jxlsContext.putVar("bracketsRegex", "[\\{\\}\"]");
- return jxlsContext;
- }
-
- public static void processTemplateWithSheets(
- InputStream templateStream, OutputStream targetStream,
- org.jxls.common.Context jxlsContext) throws IOException {
-
- Transformer transformer = TransformerFactory.createTransformer(templateStream, targetStream);
- List<Area> xlsAreas = new XlsCommentAreaBuilder(transformer).build();
- for (Area xlsArea : xlsAreas) {
- xlsArea.applyAt(new CellRef(xlsArea.getStartCellRef().getCellName()), jxlsContext);
- xlsArea.setFormulaProcessor(new StandardFormulaProcessor());
- xlsArea.processFormulas();
- }
- transformer.deleteSheet(xlsAreas.get(0).getStartCellRef().getSheetName());
- transformer.write();
- }
-
- private static TripReport calculateTrip(
- ArrayList<Position> positions, int startIndex, int endIndex, boolean ignoreOdometer) {
-
- Position startTrip = positions.get(startIndex);
- Position endTrip = positions.get(endIndex);
-
- double speedMax = 0;
- for (int i = startIndex; i <= endIndex; i++) {
- double speed = positions.get(i).getSpeed();
- if (speed > speedMax) {
- speedMax = speed;
- }
- }
-
- TripReport trip = new TripReport();
-
- long tripDuration = endTrip.getFixTime().getTime() - startTrip.getFixTime().getTime();
- long deviceId = startTrip.getDeviceId();
- trip.setDeviceId(deviceId);
- trip.setDeviceName(Context.getIdentityManager().getById(deviceId).getName());
-
- trip.setStartPositionId(startTrip.getId());
- trip.setStartLat(startTrip.getLatitude());
- trip.setStartLon(startTrip.getLongitude());
- trip.setStartTime(startTrip.getFixTime());
- String startAddress = startTrip.getAddress();
- if (startAddress == null && Context.getGeocoder() != null
- && Context.getConfig().getBoolean(Keys.GEOCODER_ON_REQUEST)) {
- startAddress = Context.getGeocoder().getAddress(startTrip.getLatitude(), startTrip.getLongitude(), null);
- }
- trip.setStartAddress(startAddress);
-
- trip.setEndPositionId(endTrip.getId());
- trip.setEndLat(endTrip.getLatitude());
- trip.setEndLon(endTrip.getLongitude());
- trip.setEndTime(endTrip.getFixTime());
- String endAddress = endTrip.getAddress();
- if (endAddress == null && Context.getGeocoder() != null
- && Context.getConfig().getBoolean(Keys.GEOCODER_ON_REQUEST)) {
- endAddress = Context.getGeocoder().getAddress(endTrip.getLatitude(), endTrip.getLongitude(), null);
- }
- trip.setEndAddress(endAddress);
-
- trip.setDistance(calculateDistance(startTrip, endTrip, !ignoreOdometer));
- trip.setDuration(tripDuration);
- if (tripDuration > 0) {
- trip.setAverageSpeed(UnitsConverter.knotsFromMps(trip.getDistance() * 1000 / tripDuration));
- }
- trip.setMaxSpeed(speedMax);
- trip.setSpentFuel(calculateFuel(startTrip, endTrip));
-
- trip.setDriverUniqueId(findDriver(startTrip, endTrip));
- trip.setDriverName(findDriverName(trip.getDriverUniqueId()));
-
- if (!ignoreOdometer
- && startTrip.getDouble(Position.KEY_ODOMETER) != 0
- && endTrip.getDouble(Position.KEY_ODOMETER) != 0) {
- trip.setStartOdometer(startTrip.getDouble(Position.KEY_ODOMETER));
- trip.setEndOdometer(endTrip.getDouble(Position.KEY_ODOMETER));
- } else {
- trip.setStartOdometer(startTrip.getDouble(Position.KEY_TOTAL_DISTANCE));
- trip.setEndOdometer(endTrip.getDouble(Position.KEY_TOTAL_DISTANCE));
- }
-
- return trip;
- }
-
- private static StopReport calculateStop(
- ArrayList<Position> positions, int startIndex, int endIndex, boolean ignoreOdometer) {
-
- Position startStop = positions.get(startIndex);
- Position endStop = positions.get(endIndex);
-
- StopReport stop = new StopReport();
-
- long deviceId = startStop.getDeviceId();
- stop.setDeviceId(deviceId);
- stop.setDeviceName(Context.getIdentityManager().getById(deviceId).getName());
-
- stop.setPositionId(startStop.getId());
- stop.setLatitude(startStop.getLatitude());
- stop.setLongitude(startStop.getLongitude());
- stop.setStartTime(startStop.getFixTime());
- String address = startStop.getAddress();
- if (address == null && Context.getGeocoder() != null
- && Context.getConfig().getBoolean(Keys.GEOCODER_ON_REQUEST)) {
- address = Context.getGeocoder().getAddress(stop.getLatitude(), stop.getLongitude(), null);
- }
- stop.setAddress(address);
-
- stop.setEndTime(endStop.getFixTime());
-
- long stopDuration = endStop.getFixTime().getTime() - startStop.getFixTime().getTime();
- stop.setDuration(stopDuration);
- stop.setSpentFuel(calculateFuel(startStop, endStop));
-
- if (startStop.getAttributes().containsKey(Position.KEY_HOURS)
- && endStop.getAttributes().containsKey(Position.KEY_HOURS)) {
- stop.setEngineHours(endStop.getLong(Position.KEY_HOURS) - startStop.getLong(Position.KEY_HOURS));
- }
-
- if (!ignoreOdometer
- && startStop.getDouble(Position.KEY_ODOMETER) != 0
- && endStop.getDouble(Position.KEY_ODOMETER) != 0) {
- stop.setStartOdometer(startStop.getDouble(Position.KEY_ODOMETER));
- stop.setEndOdometer(endStop.getDouble(Position.KEY_ODOMETER));
- } else {
- stop.setStartOdometer(startStop.getDouble(Position.KEY_TOTAL_DISTANCE));
- stop.setEndOdometer(endStop.getDouble(Position.KEY_TOTAL_DISTANCE));
- }
-
- return stop;
-
- }
-
- private static <T extends BaseReport> T calculateTripOrStop(
- ArrayList<Position> positions, int startIndex, int endIndex, boolean ignoreOdometer, Class<T> reportClass) {
-
- if (reportClass.equals(TripReport.class)) {
- return (T) calculateTrip(positions, startIndex, endIndex, ignoreOdometer);
- } else {
- return (T) calculateStop(positions, startIndex, endIndex, ignoreOdometer);
- }
- }
-
- private static boolean isMoving(ArrayList<Position> positions, int index, TripsConfig tripsConfig) {
- if (tripsConfig.getMinimalNoDataDuration() > 0) {
- boolean beforeGap = index < positions.size() - 1
- && positions.get(index + 1).getFixTime().getTime() - positions.get(index).getFixTime().getTime()
- >= tripsConfig.getMinimalNoDataDuration();
- boolean afterGap = index > 0
- && positions.get(index).getFixTime().getTime() - positions.get(index - 1).getFixTime().getTime()
- >= tripsConfig.getMinimalNoDataDuration();
- if (beforeGap || afterGap) {
- return false;
- }
- }
- if (positions.get(index).getAttributes().containsKey(Position.KEY_MOTION)
- && positions.get(index).getAttributes().get(Position.KEY_MOTION) instanceof Boolean) {
- return positions.get(index).getBoolean(Position.KEY_MOTION);
- } else {
- return positions.get(index).getSpeed() > tripsConfig.getSpeedThreshold();
- }
- }
-
- public static <T extends BaseReport> Collection<T> detectTripsAndStops(
- IdentityManager identityManager, DeviceManager deviceManager,
- Collection<Position> positionCollection,
- TripsConfig tripsConfig, boolean ignoreOdometer, Class<T> reportClass) {
-
- Collection<T> result = new ArrayList<>();
-
- ArrayList<Position> positions = new ArrayList<>(positionCollection);
- if (!positions.isEmpty()) {
- boolean trips = reportClass.equals(TripReport.class);
- MotionEventHandler motionHandler = new MotionEventHandler(identityManager, deviceManager, tripsConfig);
- DeviceState deviceState = new DeviceState();
- deviceState.setMotionState(isMoving(positions, 0, tripsConfig));
- int startEventIndex = trips == deviceState.getMotionState() ? 0 : -1;
- int startNoEventIndex = -1;
- for (int i = 0; i < positions.size(); i++) {
- Map<Event, Position> event = motionHandler.updateMotionState(deviceState, positions.get(i),
- isMoving(positions, i, tripsConfig));
- if (startEventIndex == -1
- && (trips != deviceState.getMotionState() && deviceState.getMotionPosition() != null
- || trips == deviceState.getMotionState() && event != null)) {
- startEventIndex = i;
- startNoEventIndex = -1;
- } else if (trips != deviceState.getMotionState() && startEventIndex != -1
- && deviceState.getMotionPosition() == null && event == null) {
- startEventIndex = -1;
- }
- if (startNoEventIndex == -1
- && (trips == deviceState.getMotionState() && deviceState.getMotionPosition() != null
- || trips != deviceState.getMotionState() && event != null)) {
- startNoEventIndex = i;
- } else if (startNoEventIndex != -1 && deviceState.getMotionPosition() == null && event == null) {
- startNoEventIndex = -1;
- }
- if (startEventIndex != -1 && startNoEventIndex != -1 && event != null
- && trips != deviceState.getMotionState()) {
- result.add(calculateTripOrStop(positions, startEventIndex, startNoEventIndex,
- ignoreOdometer, reportClass));
- startEventIndex = -1;
- }
- }
- if (startEventIndex != -1 && (startNoEventIndex != -1 || !trips)) {
- result.add(calculateTripOrStop(positions, startEventIndex,
- startNoEventIndex != -1 ? startNoEventIndex : positions.size() - 1,
- ignoreOdometer, reportClass));
- }
- }
-
- return result;
- }
-
-}
diff --git a/src/main/java/org/traccar/reports/Route.java b/src/main/java/org/traccar/reports/Route.java
deleted file mode 100644
index 6adb00aae..000000000
--- a/src/main/java/org/traccar/reports/Route.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.reports;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Date;
-
-import org.apache.poi.ss.util.WorkbookUtil;
-import org.traccar.Context;
-import org.traccar.model.Device;
-import org.traccar.model.Group;
-import org.traccar.model.Position;
-import org.traccar.reports.model.DeviceReport;
-
-public final class Route {
-
- private Route() {
- }
-
- public static Collection<Position> getObjects(long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
- Date from, Date to) throws SQLException {
- ReportUtils.checkPeriodLimit(from, to);
- ArrayList<Position> result = new ArrayList<>();
- for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
- Context.getPermissionsManager().checkDevice(userId, deviceId);
- result.addAll(Context.getDataManager().getPositions(deviceId, from, to));
- }
- return result;
- }
-
- public static void getExcel(OutputStream outputStream,
- long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
- Date from, Date to) throws SQLException, IOException {
- ReportUtils.checkPeriodLimit(from, to);
- ArrayList<DeviceReport> devicesRoutes = new ArrayList<>();
- ArrayList<String> sheetNames = new ArrayList<>();
- for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
- Context.getPermissionsManager().checkDevice(userId, deviceId);
- Collection<Position> positions = Context.getDataManager()
- .getPositions(deviceId, from, to);
- DeviceReport deviceRoutes = new DeviceReport();
- Device device = Context.getIdentityManager().getById(deviceId);
- deviceRoutes.setDeviceName(device.getName());
- sheetNames.add(WorkbookUtil.createSafeSheetName(deviceRoutes.getDeviceName()));
- if (device.getGroupId() != 0) {
- Group group = Context.getGroupsManager().getById(device.getGroupId());
- if (group != null) {
- deviceRoutes.setGroupName(group.getName());
- }
- }
- deviceRoutes.setObjects(positions);
- devicesRoutes.add(deviceRoutes);
- }
- String templatePath = Context.getConfig().getString("report.templatesPath",
- "templates/export/");
- try (InputStream inputStream = new FileInputStream(templatePath + "/route.xlsx")) {
- org.jxls.common.Context jxlsContext = ReportUtils.initializeContext(userId);
- jxlsContext.putVar("devices", devicesRoutes);
- jxlsContext.putVar("sheetNames", sheetNames);
- jxlsContext.putVar("from", from);
- jxlsContext.putVar("to", to);
- ReportUtils.processTemplateWithSheets(inputStream, outputStream, jxlsContext);
- }
- }
-}
diff --git a/src/main/java/org/traccar/reports/RouteReportProvider.java b/src/main/java/org/traccar/reports/RouteReportProvider.java
new file mode 100644
index 000000000..3ee651619
--- /dev/null
+++ b/src/main/java/org/traccar/reports/RouteReportProvider.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.reports;
+
+import org.apache.poi.ss.util.WorkbookUtil;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.helper.model.PositionUtil;
+import org.traccar.model.Device;
+import org.traccar.model.Group;
+import org.traccar.model.Position;
+import org.traccar.reports.common.ReportUtils;
+import org.traccar.reports.model.DeviceReportSection;
+import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Request;
+
+import javax.inject.Inject;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+
+public class RouteReportProvider {
+
+ private final Config config;
+ private final ReportUtils reportUtils;
+ private final Storage storage;
+
+ @Inject
+ public RouteReportProvider(Config config, ReportUtils reportUtils, Storage storage) {
+ this.config = config;
+ this.reportUtils = reportUtils;
+ this.storage = storage;
+ }
+
+ public Collection<Position> getObjects(long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
+ Date from, Date to) throws StorageException {
+ reportUtils.checkPeriodLimit(from, to);
+
+ ArrayList<Position> result = new ArrayList<>();
+ for (Device device: reportUtils.getAccessibleDevices(userId, deviceIds, groupIds)) {
+ result.addAll(PositionUtil.getPositions(storage, device.getId(), from, to));
+ }
+ return result;
+ }
+
+ public void getExcel(OutputStream outputStream,
+ long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
+ Date from, Date to) throws StorageException, IOException {
+ reportUtils.checkPeriodLimit(from, to);
+
+ ArrayList<DeviceReportSection> devicesRoutes = new ArrayList<>();
+ ArrayList<String> sheetNames = new ArrayList<>();
+ for (Device device: reportUtils.getAccessibleDevices(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()));
+ if (device.getGroupId() > 0) {
+ Group group = storage.getObject(Group.class, new Request(
+ new Columns.All(), new Condition.Equals("id", device.getGroupId())));
+ if (group != null) {
+ deviceRoutes.setGroupName(group.getName());
+ }
+ }
+ deviceRoutes.setObjects(positions);
+ devicesRoutes.add(deviceRoutes);
+ }
+
+ File file = Paths.get(config.getString(Keys.TEMPLATES_ROOT), "export", "route.xlsx").toFile();
+ try (InputStream inputStream = new FileInputStream(file)) {
+ var context = reportUtils.initializeContext(userId);
+ context.putVar("devices", devicesRoutes);
+ context.putVar("sheetNames", sheetNames);
+ context.putVar("from", from);
+ context.putVar("to", to);
+ reportUtils.processTemplateWithSheets(inputStream, outputStream, context);
+ }
+ }
+}
diff --git a/src/main/java/org/traccar/reports/Stops.java b/src/main/java/org/traccar/reports/Stops.java
deleted file mode 100644
index 2036b0641..000000000
--- a/src/main/java/org/traccar/reports/Stops.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
- * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.traccar.reports;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Date;
-
-import org.apache.poi.ss.util.WorkbookUtil;
-import org.traccar.Context;
-import org.traccar.Main;
-import org.traccar.database.DeviceManager;
-import org.traccar.database.IdentityManager;
-import org.traccar.model.Device;
-import org.traccar.model.Group;
-import org.traccar.reports.model.DeviceReport;
-import org.traccar.reports.model.StopReport;
-
-public final class Stops {
-
- private Stops() {
- }
-
- private static Collection<StopReport> detectStops(long deviceId, Date from, Date to) throws SQLException {
- boolean ignoreOdometer = Context.getDeviceManager()
- .lookupAttributeBoolean(deviceId, "report.ignoreOdometer", false, false, true);
-
- IdentityManager identityManager = Main.getInjector().getInstance(IdentityManager.class);
- DeviceManager deviceManager = Main.getInjector().getInstance(DeviceManager.class);
-
- return ReportUtils.detectTripsAndStops(
- identityManager, deviceManager, Context.getDataManager().getPositions(deviceId, from, to),
- Context.getTripsConfig(), ignoreOdometer, StopReport.class);
- }
-
- public static Collection<StopReport> getObjects(
- long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
- Date from, Date to) throws SQLException {
- ReportUtils.checkPeriodLimit(from, to);
- ArrayList<StopReport> result = new ArrayList<>();
- for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
- Context.getPermissionsManager().checkDevice(userId, deviceId);
- result.addAll(detectStops(deviceId, from, to));
- }
- return result;
- }
-
- public static void getExcel(
- OutputStream outputStream, long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
- Date from, Date to) throws SQLException, IOException {
- ReportUtils.checkPeriodLimit(from, to);
- ArrayList<DeviceReport> devicesStops = new ArrayList<>();
- ArrayList<String> sheetNames = new ArrayList<>();
- for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
- Context.getPermissionsManager().checkDevice(userId, deviceId);
- Collection<StopReport> stops = detectStops(deviceId, from, to);
- DeviceReport deviceStops = new DeviceReport();
- Device device = Context.getIdentityManager().getById(deviceId);
- deviceStops.setDeviceName(device.getName());
- sheetNames.add(WorkbookUtil.createSafeSheetName(deviceStops.getDeviceName()));
- if (device.getGroupId() != 0) {
- Group group = Context.getGroupsManager().getById(device.getGroupId());
- if (group != null) {
- deviceStops.setGroupName(group.getName());
- }
- }
- deviceStops.setObjects(stops);
- devicesStops.add(deviceStops);
- }
- String templatePath = Context.getConfig().getString("report.templatesPath",
- "templates/export/");
- try (InputStream inputStream = new FileInputStream(templatePath + "/stops.xlsx")) {
- org.jxls.common.Context jxlsContext = ReportUtils.initializeContext(userId);
- jxlsContext.putVar("devices", devicesStops);
- jxlsContext.putVar("sheetNames", sheetNames);
- jxlsContext.putVar("from", from);
- jxlsContext.putVar("to", to);
- ReportUtils.processTemplateWithSheets(inputStream, outputStream, jxlsContext);
- }
- }
-
-}
diff --git a/src/main/java/org/traccar/reports/StopsReportProvider.java b/src/main/java/org/traccar/reports/StopsReportProvider.java
new file mode 100644
index 000000000..ec3fd2215
--- /dev/null
+++ b/src/main/java/org/traccar/reports/StopsReportProvider.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.reports;
+
+import org.apache.poi.ss.util.WorkbookUtil;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.helper.model.PositionUtil;
+import org.traccar.model.Device;
+import org.traccar.model.Group;
+import org.traccar.reports.common.ReportUtils;
+import org.traccar.reports.model.DeviceReportSection;
+import org.traccar.reports.model.StopReportItem;
+import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Request;
+
+import javax.inject.Inject;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+
+public class StopsReportProvider {
+
+ private final Config config;
+ private final ReportUtils reportUtils;
+ private final Storage storage;
+
+ @Inject
+ public StopsReportProvider(Config config, ReportUtils reportUtils, Storage storage) {
+ this.config = config;
+ this.reportUtils = reportUtils;
+ this.storage = storage;
+ }
+
+ 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));
+ }
+ return result;
+ }
+
+ public void getExcel(
+ OutputStream outputStream, long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
+ Date from, Date to) throws StorageException, IOException {
+ reportUtils.checkPeriodLimit(from, to);
+
+ ArrayList<DeviceReportSection> devicesStops = new ArrayList<>();
+ ArrayList<String> sheetNames = new ArrayList<>();
+ for (Device device: reportUtils.getAccessibleDevices(userId, deviceIds, groupIds)) {
+ Collection<StopReportItem> stops = detectStops(device, from, to);
+ DeviceReportSection deviceStops = new DeviceReportSection();
+ deviceStops.setDeviceName(device.getName());
+ sheetNames.add(WorkbookUtil.createSafeSheetName(deviceStops.getDeviceName()));
+ if (device.getGroupId() > 0) {
+ Group group = storage.getObject(Group.class, new Request(
+ new Columns.All(), new Condition.Equals("id", device.getGroupId())));
+ if (group != null) {
+ deviceStops.setGroupName(group.getName());
+ }
+ }
+ deviceStops.setObjects(stops);
+ devicesStops.add(deviceStops);
+ }
+
+ File file = Paths.get(config.getString(Keys.TEMPLATES_ROOT), "export", "stops.xlsx").toFile();
+ try (InputStream inputStream = new FileInputStream(file)) {
+ var context = reportUtils.initializeContext(userId);
+ context.putVar("devices", devicesStops);
+ context.putVar("sheetNames", sheetNames);
+ context.putVar("from", from);
+ context.putVar("to", to);
+ reportUtils.processTemplateWithSheets(inputStream, outputStream, context);
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/reports/Summary.java b/src/main/java/org/traccar/reports/SummaryReportProvider.java
index d576ac1e6..9415f3e81 100644
--- a/src/main/java/org/traccar/reports/Summary.java
+++ b/src/main/java/org/traccar/reports/SummaryReportProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,31 +16,52 @@
*/
package org.traccar.reports;
+import org.jxls.util.JxlsHelper;
+import org.traccar.api.security.PermissionsService;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.helper.model.PositionUtil;
+import org.traccar.helper.model.UserUtil;
+import org.traccar.model.Device;
+import org.traccar.model.Position;
+import org.traccar.reports.common.ReportUtils;
+import org.traccar.reports.model.SummaryReportItem;
+import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
+
+import javax.inject.Inject;
+import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.sql.SQLException;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
-import org.jxls.util.JxlsHelper;
-import org.traccar.Context;
-import org.traccar.helper.UnitsConverter;
-import org.traccar.model.Position;
-import org.traccar.reports.model.SummaryReport;
+public class SummaryReportProvider {
-public final class Summary {
+ private final Config config;
+ private final ReportUtils reportUtils;
+ private final PermissionsService permissionsService;
+ private final Storage storage;
- private Summary() {
+ @Inject
+ public SummaryReportProvider(
+ Config config, ReportUtils reportUtils, PermissionsService permissionsService, Storage storage) {
+ this.config = config;
+ this.reportUtils = reportUtils;
+ this.permissionsService = permissionsService;
+ this.storage = storage;
}
- private static SummaryReport calculateSummaryResult(long deviceId, Collection<Position> positions) {
- SummaryReport result = new SummaryReport();
- result.setDeviceId(deviceId);
- result.setDeviceName(Context.getIdentityManager().getById(deviceId).getName());
+ private SummaryReportItem calculateSummaryResult(Device device, Collection<Position> positions) {
+ SummaryReportItem result = new SummaryReportItem();
+ result.setDeviceId(device.getId());
+ result.setDeviceName(device.getName());
if (positions != null && !positions.isEmpty()) {
Position firstPosition = null;
Position previousPosition = null;
@@ -53,14 +74,12 @@ public final class Summary {
result.setMaxSpeed(position.getSpeed());
}
}
- boolean ignoreOdometer = Context.getDeviceManager()
- .lookupAttributeBoolean(deviceId, "report.ignoreOdometer", false, false, true);
- result.setDistance(ReportUtils.calculateDistance(firstPosition, previousPosition, !ignoreOdometer));
- result.setSpentFuel(ReportUtils.calculateFuel(firstPosition, previousPosition));
+ boolean ignoreOdometer = config.getBoolean(Keys.REPORT_IGNORE_ODOMETER);
+ result.setDistance(PositionUtil.calculateDistance(firstPosition, previousPosition, !ignoreOdometer));
+ result.setSpentFuel(reportUtils.calculateFuel(firstPosition, previousPosition));
long durationMilliseconds;
- if (firstPosition.getAttributes().containsKey(Position.KEY_HOURS)
- && previousPosition.getAttributes().containsKey(Position.KEY_HOURS)) {
+ if (firstPosition.hasAttribute(Position.KEY_HOURS) && previousPosition.hasAttribute(Position.KEY_HOURS)) {
durationMilliseconds =
previousPosition.getLong(Position.KEY_HOURS) - firstPosition.getLong(Position.KEY_HOURS);
result.setEngineHours(durationMilliseconds);
@@ -90,62 +109,67 @@ public final class Summary {
return result;
}
- private static int getDay(long userId, Date date) {
- Calendar calendar = Calendar.getInstance(ReportUtils.getTimezone(userId));
+ 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);
}
- private static Collection<SummaryReport> calculateSummaryResults(
- long userId, long deviceId, Date from, Date to, boolean daily) throws SQLException {
-
- ArrayList<Position> positions = new ArrayList<>(Context.getDataManager().getPositions(deviceId, from, to));
+ private Collection<SummaryReportItem> calculateSummaryResults(
+ long userId, Device device, Date from, Date to, boolean daily) throws StorageException {
- ArrayList<SummaryReport> results = new ArrayList<>();
+ var positions = PositionUtil.getPositions(storage, device.getId(), from, to);
+ 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(deviceId, positions.subList(startIndex, i)));
+ results.add(calculateSummaryResult(device, positions.subList(startIndex, i)));
startIndex = i;
startDay = currentDay;
}
}
- results.add(calculateSummaryResult(deviceId, positions.subList(startIndex, positions.size())));
+ results.add(calculateSummaryResult(device, positions.subList(startIndex, positions.size())));
} else {
- results.add(calculateSummaryResult(deviceId, positions));
+ results.add(calculateSummaryResult(device, positions));
}
return results;
}
- public static Collection<SummaryReport> getObjects(long userId, Collection<Long> deviceIds,
- Collection<Long> groupIds, Date from, Date to, boolean daily) throws SQLException {
- ReportUtils.checkPeriodLimit(from, to);
- ArrayList<SummaryReport> result = new ArrayList<>();
- for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
- Context.getPermissionsManager().checkDevice(userId, deviceId);
- result.addAll(calculateSummaryResults(userId, deviceId, from, to, daily));
+ public Collection<SummaryReportItem> getObjects(
+ long userId, Collection<Long> deviceIds,
+ Collection<Long> groupIds, Date from, Date to, boolean daily) throws StorageException {
+ reportUtils.checkPeriodLimit(from, to);
+
+ ArrayList<SummaryReportItem> result = new ArrayList<>();
+ for (Device device: reportUtils.getAccessibleDevices(userId, deviceIds, groupIds)) {
+ Collection<SummaryReportItem> deviceResults = calculateSummaryResults(userId, device, from, to, daily);
+ for (SummaryReportItem summaryReport : deviceResults) {
+ if (summaryReport.getStartTime() != null && summaryReport.getEndTime() != null) {
+ result.add(summaryReport);
+ }
+ }
}
return result;
}
- public static void getExcel(OutputStream outputStream,
+ public void getExcel(OutputStream outputStream,
long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
- Date from, Date to, boolean daily) throws SQLException, IOException {
- ReportUtils.checkPeriodLimit(from, to);
- Collection<SummaryReport> summaries = getObjects(userId, deviceIds, groupIds, from, to, daily);
- String templatePath = Context.getConfig().getString("report.templatesPath",
- "templates/export/");
- try (InputStream inputStream = new FileInputStream(templatePath + "/summary.xlsx")) {
- org.jxls.common.Context jxlsContext = ReportUtils.initializeContext(userId);
- jxlsContext.putVar("summaries", summaries);
- jxlsContext.putVar("from", from);
- jxlsContext.putVar("to", to);
+ Date from, Date to, boolean daily) throws StorageException, IOException {
+ Collection<SummaryReportItem> summaries = getObjects(userId, deviceIds, groupIds, from, to, daily);
+
+ File file = Paths.get(config.getString(Keys.TEMPLATES_ROOT), "export", "summary.xlsx").toFile();
+ try (InputStream inputStream = new FileInputStream(file)) {
+ var context = reportUtils.initializeContext(userId);
+ context.putVar("summaries", summaries);
+ context.putVar("from", from);
+ context.putVar("to", to);
JxlsHelper.getInstance().setUseFastFormulaProcessor(false)
- .processTemplate(inputStream, outputStream, jxlsContext);
+ .processTemplate(inputStream, outputStream, context);
}
}
}
diff --git a/src/main/java/org/traccar/reports/Trips.java b/src/main/java/org/traccar/reports/Trips.java
deleted file mode 100644
index 7c0cd6921..000000000
--- a/src/main/java/org/traccar/reports/Trips.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.reports;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Date;
-
-import org.apache.poi.ss.util.WorkbookUtil;
-import org.traccar.Context;
-import org.traccar.Main;
-import org.traccar.database.DeviceManager;
-import org.traccar.database.IdentityManager;
-import org.traccar.model.Device;
-import org.traccar.model.Group;
-import org.traccar.reports.model.DeviceReport;
-import org.traccar.reports.model.TripReport;
-
-public final class Trips {
-
- private Trips() {
- }
-
- private static Collection<TripReport> detectTrips(long deviceId, Date from, Date to) throws SQLException {
- boolean ignoreOdometer = Context.getDeviceManager()
- .lookupAttributeBoolean(deviceId, "report.ignoreOdometer", false, false, true);
-
- IdentityManager identityManager = Main.getInjector().getInstance(IdentityManager.class);
- DeviceManager deviceManager = Main.getInjector().getInstance(DeviceManager.class);
-
- return ReportUtils.detectTripsAndStops(
- identityManager, deviceManager, Context.getDataManager().getPositions(deviceId, from, to),
- Context.getTripsConfig(), ignoreOdometer, TripReport.class);
- }
-
- public static Collection<TripReport> getObjects(long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
- Date from, Date to) throws SQLException {
- ReportUtils.checkPeriodLimit(from, to);
- ArrayList<TripReport> result = new ArrayList<>();
- for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
- Context.getPermissionsManager().checkDevice(userId, deviceId);
- result.addAll(detectTrips(deviceId, from, to));
- }
- return result;
- }
-
- public static void getExcel(OutputStream outputStream,
- long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
- Date from, Date to) throws SQLException, IOException {
- ReportUtils.checkPeriodLimit(from, to);
- ArrayList<DeviceReport> devicesTrips = new ArrayList<>();
- ArrayList<String> sheetNames = new ArrayList<>();
- for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
- Context.getPermissionsManager().checkDevice(userId, deviceId);
- Collection<TripReport> trips = detectTrips(deviceId, from, to);
- DeviceReport deviceTrips = new DeviceReport();
- Device device = Context.getIdentityManager().getById(deviceId);
- deviceTrips.setDeviceName(device.getName());
- sheetNames.add(WorkbookUtil.createSafeSheetName(deviceTrips.getDeviceName()));
- if (device.getGroupId() != 0) {
- Group group = Context.getGroupsManager().getById(device.getGroupId());
- if (group != null) {
- deviceTrips.setGroupName(group.getName());
- }
- }
- deviceTrips.setObjects(trips);
- devicesTrips.add(deviceTrips);
- }
- String templatePath = Context.getConfig().getString("report.templatesPath",
- "templates/export/");
- try (InputStream inputStream = new FileInputStream(templatePath + "/trips.xlsx")) {
- org.jxls.common.Context jxlsContext = ReportUtils.initializeContext(userId);
- jxlsContext.putVar("devices", devicesTrips);
- jxlsContext.putVar("sheetNames", sheetNames);
- jxlsContext.putVar("from", from);
- jxlsContext.putVar("to", to);
- ReportUtils.processTemplateWithSheets(inputStream, outputStream, jxlsContext);
- }
- }
-
-}
diff --git a/src/main/java/org/traccar/reports/TripsReportProvider.java b/src/main/java/org/traccar/reports/TripsReportProvider.java
new file mode 100644
index 000000000..265811354
--- /dev/null
+++ b/src/main/java/org/traccar/reports/TripsReportProvider.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.reports;
+
+import org.apache.poi.ss.util.WorkbookUtil;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.helper.model.PositionUtil;
+import org.traccar.model.Device;
+import org.traccar.model.Group;
+import org.traccar.reports.common.ReportUtils;
+import org.traccar.reports.model.DeviceReportSection;
+import org.traccar.reports.model.TripReportItem;
+import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Request;
+
+import javax.inject.Inject;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+
+public class TripsReportProvider {
+
+ private final Config config;
+ private final ReportUtils reportUtils;
+ private final Storage storage;
+
+ @Inject
+ public TripsReportProvider(Config config, ReportUtils reportUtils, Storage storage) {
+ this.config = config;
+ this.reportUtils = reportUtils;
+ this.storage = storage;
+ }
+
+ 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));
+ }
+ return result;
+ }
+
+ public void getExcel(OutputStream outputStream,
+ long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
+ Date from, Date to) throws StorageException, IOException {
+ reportUtils.checkPeriodLimit(from, to);
+
+ ArrayList<DeviceReportSection> devicesTrips = new ArrayList<>();
+ ArrayList<String> sheetNames = new ArrayList<>();
+ for (Device device: reportUtils.getAccessibleDevices(userId, deviceIds, groupIds)) {
+ Collection<TripReportItem> trips = detectTrips(device, from, to);
+ DeviceReportSection deviceTrips = new DeviceReportSection();
+ deviceTrips.setDeviceName(device.getName());
+ sheetNames.add(WorkbookUtil.createSafeSheetName(deviceTrips.getDeviceName()));
+ if (device.getGroupId() > 0) {
+ Group group = storage.getObject(Group.class, new Request(
+ new Columns.All(), new Condition.Equals("id", device.getGroupId())));
+ if (group != null) {
+ deviceTrips.setGroupName(group.getName());
+ }
+ }
+ deviceTrips.setObjects(trips);
+ devicesTrips.add(deviceTrips);
+ }
+
+ File file = Paths.get(config.getString(Keys.TEMPLATES_ROOT), "export", "trips.xlsx").toFile();
+ try (InputStream inputStream = new FileInputStream(file)) {
+ var context = reportUtils.initializeContext(userId);
+ context.putVar("devices", devicesTrips);
+ context.putVar("sheetNames", sheetNames);
+ context.putVar("from", from);
+ context.putVar("to", to);
+ reportUtils.processTemplateWithSheets(inputStream, outputStream, context);
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/reports/common/ReportUtils.java b/src/main/java/org/traccar/reports/common/ReportUtils.java
new file mode 100644
index 000000000..a7c420095
--- /dev/null
+++ b/src/main/java/org/traccar/reports/common/ReportUtils.java
@@ -0,0 +1,399 @@
+/*
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.reports.common;
+
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.tools.generic.DateTool;
+import org.apache.velocity.tools.generic.NumberTool;
+import org.jxls.area.Area;
+import org.jxls.builder.xls.XlsCommentAreaBuilder;
+import org.jxls.common.CellRef;
+import org.jxls.formula.StandardFormulaProcessor;
+import org.jxls.transform.Transformer;
+import org.jxls.transform.poi.PoiTransformer;
+import org.jxls.util.TransformerFactory;
+import org.traccar.api.security.PermissionsService;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.geocoder.Geocoder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.helper.model.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.Position;
+import org.traccar.model.User;
+import org.traccar.reports.model.BaseReportItem;
+import org.traccar.reports.model.StopReportItem;
+import org.traccar.reports.model.TripReportItem;
+import org.traccar.session.state.MotionProcessor;
+import org.traccar.session.state.MotionState;
+import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.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.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.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) {
+ this.config = config;
+ this.storage = storage;
+ this.permissionsService = permissionsService;
+ this.tripsConfig = tripsConfig;
+ this.velocityEngine = velocityEngine;
+ this.geocoder = geocoder;
+ }
+
+ public <T extends BaseModel> T getObject(
+ long userId, Class<T> clazz, long objectId) throws StorageException, SecurityException {
+ return storage.getObject(clazz, new Request(
+ new Columns.All(),
+ new Condition.And(
+ new Condition.Equals("id", objectId),
+ new Condition.Permission(User.class, userId, clazz))));
+ }
+
+ public void checkPeriodLimit(Date from, Date to) {
+ long limit = config.getLong(Keys.REPORT_PERIOD_LIMIT) * 1000;
+ if (limit > 0 && to.getTime() - from.getTime() > limit) {
+ throw new IllegalArgumentException("Time period exceeds the limit");
+ }
+ }
+
+ public 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();
+ }
+ return 0;
+ }
+
+ public String findDriver(Position firstPosition, Position lastPosition) {
+ if (firstPosition.hasAttribute(Position.KEY_DRIVER_UNIQUE_ID)) {
+ return firstPosition.getString(Position.KEY_DRIVER_UNIQUE_ID);
+ } else if (lastPosition.hasAttribute(Position.KEY_DRIVER_UNIQUE_ID)) {
+ return lastPosition.getString(Position.KEY_DRIVER_UNIQUE_ID);
+ }
+ return null;
+ }
+
+ public String findDriverName(String driverUniqueId) throws StorageException {
+ if (driverUniqueId != null) {
+ Driver driver = storage.getObject(Driver.class, new Request(
+ new Columns.All(),
+ new Condition.Equals("uniqueId", driverUniqueId)));
+ if (driver != null) {
+ return driver.getName();
+ }
+ }
+ return null;
+ }
+
+ public org.jxls.common.Context initializeContext(long userId) throws StorageException {
+ var server = permissionsService.getServer();
+ var user = permissionsService.getUser(userId);
+ var context = PoiTransformer.createInitialContext();
+ context.putVar("distanceUnit", UserUtil.getDistanceUnit(server, user));
+ context.putVar("speedUnit", UserUtil.getSpeedUnit(server, user));
+ context.putVar("volumeUnit", UserUtil.getVolumeUnit(server, user));
+ context.putVar("webUrl", velocityEngine.getProperty("web.url"));
+ context.putVar("dateTool", new DateTool());
+ context.putVar("numberTool", new NumberTool());
+ context.putVar("timezone", UserUtil.getTimezone(server, user));
+ context.putVar("locale", Locale.getDefault());
+ context.putVar("bracketsRegex", "[\\{\\}\"]");
+ return context;
+ }
+
+ public void processTemplateWithSheets(
+ InputStream templateStream, OutputStream targetStream, org.jxls.common.Context context) throws IOException {
+
+ Transformer transformer = TransformerFactory.createTransformer(templateStream, targetStream);
+ List<Area> xlsAreas = new XlsCommentAreaBuilder(transformer).build();
+ for (Area xlsArea : xlsAreas) {
+ xlsArea.applyAt(new CellRef(xlsArea.getStartCellRef().getCellName()), context);
+ xlsArea.setFormulaProcessor(new StandardFormulaProcessor());
+ xlsArea.processFormulas();
+ }
+ transformer.deleteSheet(xlsAreas.get(0).getStartCellRef().getSheetName());
+ transformer.write();
+ }
+
+ private TripReportItem calculateTrip(
+ Device device, ArrayList<Position> positions, int startIndex, int endIndex,
+ 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();
+ long deviceId = startTrip.getDeviceId();
+ trip.setDeviceId(deviceId);
+ trip.setDeviceName(device.getName());
+
+ trip.setStartPositionId(startTrip.getId());
+ trip.setStartLat(startTrip.getLatitude());
+ trip.setStartLon(startTrip.getLongitude());
+ trip.setStartTime(startTrip.getFixTime());
+ String startAddress = startTrip.getAddress();
+ if (startAddress == null && geocoder != null && config.getBoolean(Keys.GEOCODER_ON_REQUEST)) {
+ startAddress = geocoder.getAddress(startTrip.getLatitude(), startTrip.getLongitude(), null);
+ }
+ trip.setStartAddress(startAddress);
+
+ trip.setEndPositionId(endTrip.getId());
+ trip.setEndLat(endTrip.getLatitude());
+ trip.setEndLon(endTrip.getLongitude());
+ trip.setEndTime(endTrip.getFixTime());
+ String endAddress = endTrip.getAddress();
+ if (endAddress == null && geocoder != null && config.getBoolean(Keys.GEOCODER_ON_REQUEST)) {
+ endAddress = geocoder.getAddress(endTrip.getLatitude(), endTrip.getLongitude(), null);
+ }
+ trip.setEndAddress(endAddress);
+
+ trip.setDistance(PositionUtil.calculateDistance(startTrip, endTrip, !ignoreOdometer));
+ trip.setDuration(tripDuration);
+ if (tripDuration > 0) {
+ trip.setAverageSpeed(UnitsConverter.knotsFromMps(trip.getDistance() * 1000 / tripDuration));
+ }
+ trip.setMaxSpeed(speedMax);
+ trip.setSpentFuel(calculateFuel(startTrip, endTrip));
+
+ trip.setDriverUniqueId(findDriver(startTrip, endTrip));
+ trip.setDriverName(findDriverName(trip.getDriverUniqueId()));
+
+ if (!ignoreOdometer
+ && startTrip.getDouble(Position.KEY_ODOMETER) != 0
+ && endTrip.getDouble(Position.KEY_ODOMETER) != 0) {
+ trip.setStartOdometer(startTrip.getDouble(Position.KEY_ODOMETER));
+ trip.setEndOdometer(endTrip.getDouble(Position.KEY_ODOMETER));
+ } else {
+ trip.setStartOdometer(startTrip.getDouble(Position.KEY_TOTAL_DISTANCE));
+ trip.setEndOdometer(endTrip.getDouble(Position.KEY_TOTAL_DISTANCE));
+ }
+
+ return trip;
+ }
+
+ private StopReportItem calculateStop(
+ Device device, ArrayList<Position> positions, int startIndex, int endIndex, boolean ignoreOdometer) {
+
+ Position startStop = positions.get(startIndex);
+ Position endStop = positions.get(endIndex);
+
+ StopReportItem stop = new StopReportItem();
+
+ long deviceId = startStop.getDeviceId();
+ stop.setDeviceId(deviceId);
+ stop.setDeviceName(device.getName());
+
+ stop.setPositionId(startStop.getId());
+ stop.setLatitude(startStop.getLatitude());
+ stop.setLongitude(startStop.getLongitude());
+ stop.setStartTime(startStop.getFixTime());
+ String address = startStop.getAddress();
+ if (address == null && geocoder != null && config.getBoolean(Keys.GEOCODER_ON_REQUEST)) {
+ address = geocoder.getAddress(stop.getLatitude(), stop.getLongitude(), null);
+ }
+ stop.setAddress(address);
+
+ stop.setEndTime(endStop.getFixTime());
+
+ long stopDuration = endStop.getFixTime().getTime() - startStop.getFixTime().getTime();
+ stop.setDuration(stopDuration);
+ stop.setSpentFuel(calculateFuel(startStop, endStop));
+
+ if (startStop.hasAttribute(Position.KEY_HOURS) && endStop.hasAttribute(Position.KEY_HOURS)) {
+ stop.setEngineHours(endStop.getLong(Position.KEY_HOURS) - startStop.getLong(Position.KEY_HOURS));
+ }
+
+ if (!ignoreOdometer
+ && startStop.getDouble(Position.KEY_ODOMETER) != 0
+ && endStop.getDouble(Position.KEY_ODOMETER) != 0) {
+ stop.setStartOdometer(startStop.getDouble(Position.KEY_ODOMETER));
+ stop.setEndOdometer(endStop.getDouble(Position.KEY_ODOMETER));
+ } else {
+ stop.setStartOdometer(startStop.getDouble(Position.KEY_TOTAL_DISTANCE));
+ stop.setEndOdometer(endStop.getDouble(Position.KEY_TOTAL_DISTANCE));
+ }
+
+ return stop;
+
+ }
+
+ @SuppressWarnings("unchecked")
+ private <T extends BaseReportItem> T calculateTripOrStop(
+ Device device, ArrayList<Position> positions, int startIndex, int endIndex,
+ boolean ignoreOdometer, Class<T> reportClass) throws StorageException {
+
+ if (reportClass.equals(TripReportItem.class)) {
+ return (T) calculateTrip(device, positions, startIndex, endIndex, ignoreOdometer);
+ } else {
+ return (T) calculateStop(device, positions, startIndex, endIndex, ignoreOdometer);
+ }
+ }
+
+ private boolean isMoving(ArrayList<Position> positions, int index, TripsConfig tripsConfig) {
+ if (tripsConfig.getMinimalNoDataDuration() > 0) {
+ boolean beforeGap = index < positions.size() - 1
+ && positions.get(index + 1).getFixTime().getTime() - positions.get(index).getFixTime().getTime()
+ >= tripsConfig.getMinimalNoDataDuration();
+ boolean afterGap = index > 0
+ && positions.get(index).getFixTime().getTime() - positions.get(index - 1).getFixTime().getTime()
+ >= tripsConfig.getMinimalNoDataDuration();
+ if (beforeGap || afterGap) {
+ return false;
+ }
+ }
+ 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 {
+
+ Collection<T> result = new ArrayList<>();
+
+ ArrayList<Position> positions = new ArrayList<>(positionCollection);
+ if (!positions.isEmpty()) {
+ boolean trips = reportClass.equals(TripReportItem.class);
+
+ MotionState motionState = new MotionState();
+ boolean initialValue = isMoving(positions, 0, tripsConfig);
+ motionState.setMotionStreak(initialValue);
+ motionState.setMotionState(initialValue);
+
+ boolean detected = trips == motionState.getMotionState();
+ 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;
+ startNoEventIndex = -1;
+ } else {
+ startNoEventIndex = i;
+ }
+ }
+
+ MotionProcessor.updateState(motionState, positions.get(i), motion, tripsConfig);
+ if (motionState.getEvent() != null) {
+ if (motion == trips) {
+ detected = true;
+ startNoEventIndex = -1;
+ } else if (startEventIndex >= 0 && startNoEventIndex >= 0) {
+ result.add(calculateTripOrStop(
+ device, positions, startEventIndex, startNoEventIndex, ignoreOdometer, reportClass));
+ detected = false;
+ startEventIndex = -1;
+ startNoEventIndex = -1;
+ }
+ }
+ }
+ if (startEventIndex >= 0 && startEventIndex < positions.size() - 1) {
+ int endIndex = startNoEventIndex >= 0 ? startNoEventIndex : positions.size() - 1;
+ result.add(calculateTripOrStop(
+ device, positions, startEventIndex, endIndex, ignoreOdometer, reportClass));
+ }
+ }
+
+ return result;
+ }
+
+}
diff --git a/src/main/java/org/traccar/reports/model/TripsConfig.java b/src/main/java/org/traccar/reports/common/TripsConfig.java
index 0f0c615d3..52db97b74 100644
--- a/src/main/java/org/traccar/reports/model/TripsConfig.java
+++ b/src/main/java/org/traccar/reports/common/TripsConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,14 +14,31 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.reports.model;
+package org.traccar.reports.common;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton
public class TripsConfig {
- public TripsConfig() {
+ @Inject
+ public TripsConfig(Config config) {
+ 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));
}
- public TripsConfig(double minimalTripDistance, long minimalTripDuration, long minimalParkingDuration,
+ public TripsConfig(
+ double minimalTripDistance, long minimalTripDuration, long minimalParkingDuration,
long minimalNoDataDuration, boolean useIgnition, boolean processInvalidPositions, double speedThreshold) {
this.minimalTripDistance = minimalTripDistance;
this.minimalTripDuration = minimalTripDuration;
@@ -32,74 +49,46 @@ public class TripsConfig {
this.speedThreshold = speedThreshold;
}
- private double minimalTripDistance;
+ private final double minimalTripDistance;
public double getMinimalTripDistance() {
return minimalTripDistance;
}
- public void setMinimalTripDistance(double minimalTripDistance) {
- this.minimalTripDistance = minimalTripDistance;
- }
-
- private long minimalTripDuration;
+ private final long minimalTripDuration;
public long getMinimalTripDuration() {
return minimalTripDuration;
}
- public void setMinimalTripDuration(long minimalTripDuration) {
- this.minimalTripDuration = minimalTripDuration;
- }
-
- private long minimalParkingDuration;
+ private final long minimalParkingDuration;
public long getMinimalParkingDuration() {
return minimalParkingDuration;
}
- public void setMinimalParkingDuration(long minimalParkingDuration) {
- this.minimalParkingDuration = minimalParkingDuration;
- }
-
- private long minimalNoDataDuration;
+ private final long minimalNoDataDuration;
public long getMinimalNoDataDuration() {
return minimalNoDataDuration;
}
- public void setMinimalNoDataDuration(long minimalNoDataDuration) {
- this.minimalNoDataDuration = minimalNoDataDuration;
- }
-
- private boolean useIgnition;
+ private final boolean useIgnition;
public boolean getUseIgnition() {
return useIgnition;
}
- public void setUseIgnition(boolean useIgnition) {
- this.useIgnition = useIgnition;
- }
-
- private boolean processInvalidPositions;
+ private final boolean processInvalidPositions;
public boolean getProcessInvalidPositions() {
return processInvalidPositions;
}
- public void setProcessInvalidPositions(boolean processInvalidPositions) {
- this.processInvalidPositions = processInvalidPositions;
- }
-
- private double speedThreshold;
+ private final double speedThreshold;
public double getSpeedThreshold() {
return speedThreshold;
}
- public void setSpeedThreshold(double speedThreshold) {
- this.speedThreshold = speedThreshold;
- }
-
}
diff --git a/src/main/java/org/traccar/reports/model/BaseReport.java b/src/main/java/org/traccar/reports/model/BaseReportItem.java
index 928c0557d..6e270dfe3 100644
--- a/src/main/java/org/traccar/reports/model/BaseReport.java
+++ b/src/main/java/org/traccar/reports/model/BaseReportItem.java
@@ -18,7 +18,7 @@ package org.traccar.reports.model;
import java.util.Date;
-public class BaseReport {
+public class BaseReportItem {
private long deviceId;
diff --git a/src/main/java/org/traccar/reports/model/DeviceReport.java b/src/main/java/org/traccar/reports/model/DeviceReportSection.java
index 932753d15..ffc4d774f 100644
--- a/src/main/java/org/traccar/reports/model/DeviceReport.java
+++ b/src/main/java/org/traccar/reports/model/DeviceReportSection.java
@@ -20,7 +20,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
-public class DeviceReport {
+public class DeviceReportSection {
private String deviceName;
diff --git a/src/main/java/org/traccar/reports/model/StopReport.java b/src/main/java/org/traccar/reports/model/StopReportItem.java
index e20f2c503..3c35bdc21 100644
--- a/src/main/java/org/traccar/reports/model/StopReport.java
+++ b/src/main/java/org/traccar/reports/model/StopReportItem.java
@@ -16,7 +16,7 @@
*/
package org.traccar.reports.model;
-public class StopReport extends BaseReport {
+public class StopReportItem extends BaseReportItem {
private long positionId;
diff --git a/src/main/java/org/traccar/reports/model/SummaryReport.java b/src/main/java/org/traccar/reports/model/SummaryReportItem.java
index 886f8b9e2..44a15cf1d 100644
--- a/src/main/java/org/traccar/reports/model/SummaryReport.java
+++ b/src/main/java/org/traccar/reports/model/SummaryReportItem.java
@@ -16,7 +16,7 @@
*/
package org.traccar.reports.model;
-public class SummaryReport extends BaseReport {
+public class SummaryReportItem extends BaseReportItem {
private long engineHours; // milliseconds
diff --git a/src/main/java/org/traccar/reports/model/TripReport.java b/src/main/java/org/traccar/reports/model/TripReportItem.java
index 151c34bd5..332a34cca 100644
--- a/src/main/java/org/traccar/reports/model/TripReport.java
+++ b/src/main/java/org/traccar/reports/model/TripReportItem.java
@@ -16,7 +16,7 @@
*/
package org.traccar.reports.model;
-public class TripReport extends BaseReport {
+public class TripReportItem extends BaseReportItem {
private long startPositionId;
diff --git a/src/main/java/org/traccar/schedule/ScheduleManager.java b/src/main/java/org/traccar/schedule/ScheduleManager.java
index 5d5054100..6412a186a 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 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2020 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,29 +15,39 @@
*/
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 java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
-public class ScheduleManager {
+@Singleton
+public class ScheduleManager implements LifecycleObject {
+ private final Injector injector;
private ScheduledExecutorService executor;
- public void start() {
+ @Inject
+ public ScheduleManager(Injector injector) {
+ this.injector = injector;
+ }
+ @Override
+ public void start() {
executor = Executors.newSingleThreadScheduledExecutor();
-
- new TaskDeviceInactivityCheck().schedule(executor);
- new TaskWebSocketKeepalive().schedule(executor);
-
+ List.of(TaskDeviceInactivityCheck.class, TaskWebSocketKeepalive.class, TaskHealthCheck.class)
+ .forEach(task -> injector.getInstance(task).schedule(executor));
}
+ @Override
public void stop() {
-
if (executor != null) {
executor.shutdown();
executor = null;
}
-
}
}
diff --git a/src/main/java/org/traccar/schedule/ScheduleTask.java b/src/main/java/org/traccar/schedule/ScheduleTask.java
new file mode 100644
index 000000000..1b537213b
--- /dev/null
+++ b/src/main/java/org/traccar/schedule/ScheduleTask.java
@@ -0,0 +1,22 @@
+/*
+ * 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.schedule;
+
+import java.util.concurrent.ScheduledExecutorService;
+
+public interface ScheduleTask extends Runnable {
+ void schedule(ScheduledExecutorService executor);
+}
diff --git a/src/main/java/org/traccar/schedule/TaskDeviceInactivityCheck.java b/src/main/java/org/traccar/schedule/TaskDeviceInactivityCheck.java
index 80641d7d4..57c64dc5b 100644
--- a/src/main/java/org/traccar/schedule/TaskDeviceInactivityCheck.java
+++ b/src/main/java/org/traccar/schedule/TaskDeviceInactivityCheck.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2020 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,17 +15,26 @@
*/
package org.traccar.schedule;
-import org.traccar.Context;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.database.NotificationManager;
import org.traccar.model.Device;
import org.traccar.model.Event;
import org.traccar.model.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 java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
-public class TaskDeviceInactivityCheck implements Runnable {
+public class TaskDeviceInactivityCheck implements ScheduleTask {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TaskDeviceInactivityCheck.class);
public static final String ATTRIBUTE_DEVICE_INACTIVITY_START = "deviceInactivityStart";
public static final String ATTRIBUTE_DEVICE_INACTIVITY_PERIOD = "deviceInactivityPeriod";
@@ -33,6 +42,16 @@ public class TaskDeviceInactivityCheck implements Runnable {
private static final long CHECK_PERIOD_MINUTES = 15;
+ private final Storage storage;
+ private final NotificationManager notificationManager;
+
+ @Inject
+ public TaskDeviceInactivityCheck(Storage storage, NotificationManager notificationManager) {
+ this.storage = storage;
+ this.notificationManager = notificationManager;
+ }
+
+ @Override
public void schedule(ScheduledExecutorService executor) {
executor.scheduleAtFixedRate(this, CHECK_PERIOD_MINUTES, CHECK_PERIOD_MINUTES, TimeUnit.MINUTES);
}
@@ -43,15 +62,20 @@ public class TaskDeviceInactivityCheck implements Runnable {
long checkPeriod = TimeUnit.MINUTES.toMillis(CHECK_PERIOD_MINUTES);
Map<Event, Position> events = new HashMap<>();
- for (Device device : Context.getDeviceManager().getAllDevices()) {
- if (device.getLastUpdate() != null && checkDevice(device, currentTime, checkPeriod)) {
- Event event = new Event(Event.TYPE_DEVICE_INACTIVE, device.getId());
- event.set(ATTRIBUTE_LAST_UPDATE, device.getLastUpdate().getTime());
- events.put(event, null);
+
+ try {
+ for (Device device : storage.getObjects(Device.class, new Request(new Columns.All()))) {
+ if (device.getLastUpdate() != null && checkDevice(device, currentTime, checkPeriod)) {
+ Event event = new Event(Event.TYPE_DEVICE_INACTIVE, device.getId());
+ event.set(ATTRIBUTE_LAST_UPDATE, device.getLastUpdate().getTime());
+ events.put(event, null);
+ }
}
+ } catch (StorageException e) {
+ LOGGER.warn("Get devices error", e);
}
- Context.getNotificationManager().updateEvents(events);
+ notificationManager.updateEvents(events);
}
private boolean checkDevice(Device device, long currentTime, long checkPeriod) {
diff --git a/src/main/java/org/traccar/api/HealthCheckService.java b/src/main/java/org/traccar/schedule/TaskHealthCheck.java
index 0182cc358..a8c9873ce 100644
--- a/src/main/java/org/traccar/api/HealthCheckService.java
+++ b/src/main/java/org/traccar/schedule/TaskHealthCheck.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2020 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,28 +13,37 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.api;
+package org.traccar.schedule;
import com.sun.jna.Library;
import com.sun.jna.Native;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.traccar.Context;
+import org.traccar.config.Config;
import org.traccar.config.Keys;
-import java.util.TimerTask;
+import javax.inject.Inject;
+import javax.ws.rs.client.Client;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
-public class HealthCheckService {
+public class TaskHealthCheck implements ScheduleTask {
- private static final Logger LOGGER = LoggerFactory.getLogger(HealthCheckService.class);
+ private static final Logger LOGGER = LoggerFactory.getLogger(TaskHealthCheck.class);
+
+ private final Config config;
+ private final Client client;
private SystemD systemD;
private boolean enabled;
private long period;
- public HealthCheckService() {
- if (!Context.getConfig().getBoolean(Keys.WEB_DISABLE_HEALTH_CHECK)
+ @Inject
+ public TaskHealthCheck(Config config, Client client) {
+ this.config = config;
+ this.client = client;
+ if (!config.getBoolean(Keys.WEB_DISABLE_HEALTH_CHECK)
&& System.getProperty("os.name").toLowerCase().startsWith("linux")) {
try {
systemD = Native.load("systemd", SystemD.class);
@@ -52,36 +61,31 @@ public class HealthCheckService {
}
}
- public boolean isEnabled() {
- return enabled;
- }
-
- public long getPeriod() {
- return period;
- }
-
private String getUrl() {
- String address = Context.getConfig().getString(Keys.WEB_ADDRESS, "localhost");
- int port = Context.getConfig().getInteger(Keys.WEB_PORT);
+ String address = config.getString(Keys.WEB_ADDRESS, "localhost");
+ int port = config.getInteger(Keys.WEB_PORT);
return "http://" + address + ":" + port + "/api/server";
}
- public TimerTask createTask() {
- return new TimerTask() {
- @Override
- public void run() {
- LOGGER.debug("Health check running");
- int status = Context.getClient().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);
- }
- } else {
- LOGGER.warn("Health check failed with status {}", status);
- }
+ @Override
+ public void schedule(ScheduledExecutorService executor) {
+ if (enabled) {
+ executor.scheduleAtFixedRate(this, period, period, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ @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);
}
- };
+ } else {
+ LOGGER.warn("Health check failed with status {}", status);
+ }
}
interface SystemD extends Library {
diff --git a/src/main/java/org/traccar/schedule/TaskWebSocketKeepalive.java b/src/main/java/org/traccar/schedule/TaskWebSocketKeepalive.java
index 953b0efea..e6c2e8b6d 100644
--- a/src/main/java/org/traccar/schedule/TaskWebSocketKeepalive.java
+++ b/src/main/java/org/traccar/schedule/TaskWebSocketKeepalive.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,22 +15,31 @@
*/
package org.traccar.schedule;
-import org.traccar.Context;
+import org.traccar.session.ConnectionManager;
+import javax.inject.Inject;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
-public class TaskWebSocketKeepalive implements Runnable {
+public class TaskWebSocketKeepalive implements ScheduleTask {
private static final long PERIOD_SECONDS = 55;
+ private final ConnectionManager connectionManager;
+
+ @Inject
+ public TaskWebSocketKeepalive(ConnectionManager connectionManager) {
+ this.connectionManager = connectionManager;
+ }
+
+ @Override
public void schedule(ScheduledExecutorService executor) {
executor.scheduleAtFixedRate(this, PERIOD_SECONDS, PERIOD_SECONDS, TimeUnit.SECONDS);
}
@Override
public void run() {
- Context.getConnectionManager().sendKeepalive();
+ connectionManager.sendKeepalive();
}
}
diff --git a/src/main/java/org/traccar/session/ConnectionManager.java b/src/main/java/org/traccar/session/ConnectionManager.java
new file mode 100644
index 000000000..37a42d827
--- /dev/null
+++ b/src/main/java/org/traccar/session/ConnectionManager.java
@@ -0,0 +1,374 @@
+/*
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.session;
+
+import io.netty.channel.Channel;
+import io.netty.util.Timeout;
+import io.netty.util.Timer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.Protocol;
+import org.traccar.broadcast.BroadcastInterface;
+import org.traccar.broadcast.BroadcastService;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.database.DeviceLookupService;
+import org.traccar.database.NotificationManager;
+import org.traccar.model.BaseModel;
+import org.traccar.model.Device;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+import org.traccar.model.User;
+import org.traccar.session.cache.CacheManager;
+import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Request;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+@Singleton
+public class ConnectionManager implements BroadcastInterface {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ConnectionManager.class);
+
+ private final long deviceTimeout;
+
+ private final Map<Long, DeviceSession> sessionsByDeviceId = new ConcurrentHashMap<>();
+ private final Map<Endpoint, Map<String, DeviceSession>> sessionsByEndpoint = new ConcurrentHashMap<>();
+
+ private final Config config;
+ private final CacheManager cacheManager;
+ private final Storage storage;
+ private final NotificationManager notificationManager;
+ private final Timer timer;
+ private final BroadcastService broadcastService;
+ private final DeviceLookupService deviceLookupService;
+
+ private final Map<Long, Set<UpdateListener>> listeners = new HashMap<>();
+ private final Map<Long, Set<Long>> userDevices = new HashMap<>();
+ private final Map<Long, Set<Long>> deviceUsers = new HashMap<>();
+
+ private final Map<Long, Timeout> timeouts = new ConcurrentHashMap<>();
+
+ @Inject
+ public ConnectionManager(
+ Config config, CacheManager cacheManager, Storage storage,
+ NotificationManager notificationManager, Timer timer, BroadcastService broadcastService,
+ DeviceLookupService deviceLookupService) {
+ this.config = config;
+ this.cacheManager = cacheManager;
+ this.storage = storage;
+ this.notificationManager = notificationManager;
+ this.timer = timer;
+ this.broadcastService = broadcastService;
+ this.deviceLookupService = deviceLookupService;
+ deviceTimeout = config.getLong(Keys.STATUS_TIMEOUT);
+ broadcastService.registerListener(this);
+ }
+
+ public DeviceSession getDeviceSession(long deviceId) {
+ return sessionsByDeviceId.get(deviceId);
+ }
+
+ public DeviceSession getDeviceSession(
+ Protocol protocol, Channel channel, SocketAddress remoteAddress,
+ String... uniqueIds) throws StorageException {
+
+ Endpoint endpoint = new Endpoint(channel, remoteAddress);
+ Map<String, DeviceSession> endpointSessions = sessionsByEndpoint.getOrDefault(
+ endpoint, new ConcurrentHashMap<>());
+
+ uniqueIds = Arrays.stream(uniqueIds).filter(Objects::nonNull).toArray(String[]::new);
+ if (uniqueIds.length > 0) {
+ for (String uniqueId : uniqueIds) {
+ DeviceSession deviceSession = endpointSessions.get(uniqueId);
+ if (deviceSession != null) {
+ return deviceSession;
+ }
+ }
+ } else {
+ return endpointSessions.values().stream().findAny().orElse(null);
+ }
+
+ Device device = deviceLookupService.lookup(uniqueIds);
+
+ if (device == null && config.getBoolean(Keys.DATABASE_REGISTER_UNKNOWN)) {
+ device = addUnknownDevice(uniqueIds[0]);
+ }
+
+ if (device != null) {
+ device.checkDisabled();
+
+ DeviceSession oldSession = sessionsByDeviceId.remove(device.getId());
+ if (oldSession != null) {
+ Endpoint oldEndpoint = new Endpoint(oldSession.getChannel(), oldSession.getRemoteAddress());
+ Map<String, DeviceSession> oldEndpointSessions = sessionsByEndpoint.get(oldEndpoint);
+ if (oldEndpointSessions != null && oldEndpointSessions.size() > 1) {
+ oldEndpointSessions.remove(device.getUniqueId());
+ } else {
+ sessionsByEndpoint.remove(oldEndpoint);
+ }
+ }
+
+ DeviceSession deviceSession = new DeviceSession(
+ device.getId(), device.getUniqueId(), protocol, channel, remoteAddress);
+ endpointSessions.put(device.getUniqueId(), deviceSession);
+ sessionsByEndpoint.put(endpoint, endpointSessions);
+ sessionsByDeviceId.put(device.getId(), deviceSession);
+
+ if (oldSession == null) {
+ cacheManager.addDevice(device.getId());
+ }
+
+ return deviceSession;
+ } else {
+ LOGGER.warn("Unknown device - " + String.join(" ", uniqueIds)
+ + " (" + ((InetSocketAddress) remoteAddress).getHostString() + ")");
+ return null;
+ }
+ }
+
+ private Device addUnknownDevice(String uniqueId) {
+ Device device = new Device();
+ device.setName(uniqueId);
+ device.setUniqueId(uniqueId);
+ device.setCategory(config.getString(Keys.DATABASE_REGISTER_UNKNOWN_DEFAULT_CATEGORY));
+
+ long defaultGroupId = config.getLong(Keys.DATABASE_REGISTER_UNKNOWN_DEFAULT_GROUP_ID);
+ if (defaultGroupId != 0) {
+ device.setGroupId(defaultGroupId);
+ }
+
+ try {
+ device.setId(storage.addObject(device, new Request(new Columns.Exclude("id"))));
+ LOGGER.info("Automatically registered " + uniqueId);
+ return device;
+ } catch (StorageException e) {
+ LOGGER.warn("Automatic registration failed", e);
+ return null;
+ }
+ }
+
+ public void deviceDisconnected(Channel channel, boolean supportsOffline) {
+ Endpoint endpoint = new Endpoint(channel, channel.remoteAddress());
+ Map<String, DeviceSession> endpointSessions = sessionsByEndpoint.remove(endpoint);
+ if (endpointSessions != null) {
+ for (DeviceSession deviceSession : endpointSessions.values()) {
+ if (supportsOffline) {
+ updateDevice(deviceSession.getDeviceId(), Device.STATUS_OFFLINE, null);
+ }
+ sessionsByDeviceId.remove(deviceSession.getDeviceId());
+ cacheManager.removeDevice(deviceSession.getDeviceId());
+ }
+ }
+ }
+
+ public void deviceUnknown(long deviceId) {
+ updateDevice(deviceId, Device.STATUS_UNKNOWN, null);
+ removeDeviceSession(deviceId);
+ }
+
+ private void removeDeviceSession(long deviceId) {
+ DeviceSession deviceSession = sessionsByDeviceId.remove(deviceId);
+ if (deviceSession != null) {
+ cacheManager.removeDevice(deviceId);
+ Endpoint endpoint = new Endpoint(deviceSession.getChannel(), deviceSession.getRemoteAddress());
+ sessionsByEndpoint.computeIfPresent(endpoint, (e, sessions) -> {
+ sessions.remove(deviceSession.getUniqueId());
+ return sessions.isEmpty() ? null : sessions;
+ });
+ }
+ }
+
+ public void updateDevice(long deviceId, String status, Date time) {
+ Device device = cacheManager.getObject(Device.class, deviceId);
+ if (device == null) {
+ try {
+ device = storage.getObject(Device.class, new Request(
+ new Columns.All(), new Condition.Equals("id", deviceId)));
+ } catch (StorageException e) {
+ LOGGER.warn("Failed to get device", e);
+ }
+ if (device == null) {
+ return;
+ }
+ }
+
+ String oldStatus = device.getStatus();
+ device.setStatus(status);
+
+ if (!status.equals(oldStatus)) {
+ String eventType;
+ Map<Event, Position> events = new HashMap<>();
+ switch (status) {
+ case Device.STATUS_ONLINE:
+ eventType = Event.TYPE_DEVICE_ONLINE;
+ break;
+ case Device.STATUS_UNKNOWN:
+ eventType = Event.TYPE_DEVICE_UNKNOWN;
+ break;
+ default:
+ eventType = Event.TYPE_DEVICE_OFFLINE;
+ break;
+ }
+ events.put(new Event(eventType, deviceId), null);
+ notificationManager.updateEvents(events);
+ }
+
+ if (time != null) {
+ device.setLastUpdate(time);
+ }
+
+ Timeout timeout = timeouts.remove(deviceId);
+ if (timeout != null) {
+ timeout.cancel();
+ }
+
+ if (status.equals(Device.STATUS_ONLINE)) {
+ timeouts.put(deviceId, timer.newTimeout(timeout1 -> {
+ if (!timeout1.isCancelled()) {
+ deviceUnknown(deviceId);
+ }
+ }, deviceTimeout, TimeUnit.SECONDS));
+ }
+
+ try {
+ storage.updateObject(device, new Request(
+ new Columns.Include("status", "lastUpdate"),
+ new Condition.Equals("id", deviceId)));
+ } catch (StorageException e) {
+ LOGGER.warn("Update device status error", e);
+ }
+
+ updateDevice(true, device);
+ }
+
+ public synchronized void sendKeepalive() {
+ for (Set<UpdateListener> userListeners : listeners.values()) {
+ for (UpdateListener listener : userListeners) {
+ listener.onKeepalive();
+ }
+ }
+ }
+
+ @Override
+ public synchronized void updateDevice(boolean local, Device device) {
+ if (local) {
+ broadcastService.updateDevice(true, device);
+ } else if (Device.STATUS_ONLINE.equals(device.getStatus())) {
+ timeouts.remove(device.getId());
+ removeDeviceSession(device.getId());
+ }
+ for (long userId : deviceUsers.getOrDefault(device.getId(), Collections.emptySet())) {
+ if (listeners.containsKey(userId)) {
+ for (UpdateListener listener : listeners.get(userId)) {
+ listener.onUpdateDevice(device);
+ }
+ }
+ }
+ }
+
+ @Override
+ public synchronized void updatePosition(boolean local, Position position) {
+ if (local) {
+ broadcastService.updatePosition(true, position);
+ }
+ for (long userId : deviceUsers.getOrDefault(position.getDeviceId(), Collections.emptySet())) {
+ if (listeners.containsKey(userId)) {
+ for (UpdateListener listener : listeners.get(userId)) {
+ listener.onUpdatePosition(position);
+ }
+ }
+ }
+ }
+
+ @Override
+ public synchronized void updateEvent(boolean local, long userId, Event event) {
+ if (local) {
+ broadcastService.updateEvent(true, userId, event);
+ }
+ if (listeners.containsKey(userId)) {
+ for (UpdateListener listener : listeners.get(userId)) {
+ listener.onUpdateEvent(event);
+ }
+ }
+ }
+
+ @Override
+ public synchronized void invalidatePermission(
+ boolean local,
+ Class<? extends BaseModel> clazz1, long id1,
+ Class<? extends BaseModel> clazz2, long id2) {
+ if (clazz1.equals(User.class) && clazz2.equals(Device.class)) {
+ if (listeners.containsKey(id1)) {
+ userDevices.get(id1).add(id2);
+ deviceUsers.put(id2, new HashSet<>(List.of(id1)));
+ }
+ }
+ }
+
+ public interface UpdateListener {
+ void onKeepalive();
+ void onUpdateDevice(Device device);
+ void onUpdatePosition(Position position);
+ void onUpdateEvent(Event event);
+ }
+
+ public synchronized void addListener(long userId, UpdateListener listener) throws StorageException {
+ var set = listeners.get(userId);
+ if (set == null) {
+ set = new HashSet<>();
+ listeners.put(userId, set);
+
+ var devices = storage.getObjects(Device.class, new Request(
+ new Columns.Include("id"), new Condition.Permission(User.class, userId, Device.class)));
+ userDevices.put(userId, devices.stream().map(BaseModel::getId).collect(Collectors.toSet()));
+ devices.forEach(device -> deviceUsers.computeIfAbsent(device.getId(), id -> new HashSet<>()).add(userId));
+ }
+ set.add(listener);
+ }
+
+ public synchronized void removeListener(long userId, UpdateListener listener) {
+ var set = listeners.get(userId);
+ set.remove(listener);
+ if (set.isEmpty()) {
+ listeners.remove(userId);
+
+ userDevices.remove(userId).forEach(deviceId -> deviceUsers.computeIfPresent(deviceId, (x, userIds) -> {
+ userIds.remove(userId);
+ return userIds.isEmpty() ? null : userIds;
+ }));
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/database/ActiveDevice.java b/src/main/java/org/traccar/session/DeviceSession.java
index c05d56ad2..009f90f5a 100644
--- a/src/main/java/org/traccar/database/ActiveDevice.java
+++ b/src/main/java/org/traccar/session/DeviceSession.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.database;
+package org.traccar.session;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.HttpRequestDecoder;
@@ -22,37 +22,69 @@ import org.traccar.Protocol;
import org.traccar.model.Command;
import java.net.SocketAddress;
+import java.util.HashMap;
+import java.util.Map;
-public class ActiveDevice {
+public class DeviceSession {
private final long deviceId;
+ private final String uniqueId;
private final Protocol protocol;
private final Channel channel;
private final SocketAddress remoteAddress;
- private final boolean supportsLiveCommands;
- public ActiveDevice(long deviceId, Protocol protocol, Channel channel, SocketAddress remoteAddress) {
+ public DeviceSession(
+ long deviceId, String uniqueId, Protocol protocol, Channel channel, SocketAddress remoteAddress) {
this.deviceId = deviceId;
+ this.uniqueId = uniqueId;
this.protocol = protocol;
this.channel = channel;
this.remoteAddress = remoteAddress;
- supportsLiveCommands = BasePipelineFactory.getHandler(channel.pipeline(), HttpRequestDecoder.class) == null;
+ }
+
+ public long getDeviceId() {
+ return deviceId;
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
}
public Channel getChannel() {
return channel;
}
- public long getDeviceId() {
- return deviceId;
+ public SocketAddress getRemoteAddress() {
+ return remoteAddress;
}
public boolean supportsLiveCommands() {
- return supportsLiveCommands;
+ return BasePipelineFactory.getHandler(channel.pipeline(), HttpRequestDecoder.class) == null;
}
public void sendCommand(Command command) {
protocol.sendDataCommand(channel, remoteAddress, command);
}
+ public static final String KEY_TIMEZONE = "timezone";
+
+ private final Map<String, Object> locals = new HashMap<>();
+
+ public boolean contains(String key) {
+ return locals.containsKey(key);
+ }
+
+ public void set(String key, Object value) {
+ if (value != null) {
+ locals.put(key, value);
+ } else {
+ locals.remove(key);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T get(String key) {
+ return (T) locals.get(key);
+ }
+
}
diff --git a/src/main/java/org/traccar/session/Endpoint.java b/src/main/java/org/traccar/session/Endpoint.java
new file mode 100644
index 000000000..76aac3444
--- /dev/null
+++ b/src/main/java/org/traccar/session/Endpoint.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.session;
+
+import io.netty.channel.Channel;
+
+import java.net.SocketAddress;
+import java.util.Objects;
+
+public class Endpoint {
+
+ private final Channel channel;
+ private final SocketAddress remoteAddress;
+
+ public Endpoint(Channel channel, SocketAddress remoteAddress) {
+ this.channel = channel;
+ this.remoteAddress = remoteAddress;
+ }
+
+ public Channel getChannel() {
+ return channel;
+ }
+
+ public SocketAddress getRemoteAddress() {
+ return remoteAddress;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Endpoint endpoint = (Endpoint) o;
+ return channel.equals(endpoint.channel) && remoteAddress.equals(endpoint.remoteAddress);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(channel, remoteAddress);
+ }
+
+}
diff --git a/src/main/java/org/traccar/session/cache/CacheKey.java b/src/main/java/org/traccar/session/cache/CacheKey.java
new file mode 100644
index 000000000..23145e34b
--- /dev/null
+++ b/src/main/java/org/traccar/session/cache/CacheKey.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.session.cache;
+
+import org.traccar.model.BaseModel;
+
+import java.util.Objects;
+
+class CacheKey {
+
+ private final Class<? extends BaseModel> clazz;
+ private final long id;
+
+ CacheKey(BaseModel object) {
+ this(object.getClass(), object.getId());
+ }
+
+ CacheKey(Class<? extends BaseModel> clazz, long id) {
+ this.clazz = clazz;
+ this.id = id;
+ }
+
+ public boolean classIs(Class<? extends BaseModel> clazz) {
+ return clazz.equals(this.clazz);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ CacheKey cacheKey = (CacheKey) o;
+ return id == cacheKey.id && Objects.equals(clazz, cacheKey.clazz);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(clazz, id);
+ }
+
+}
diff --git a/src/main/java/org/traccar/session/cache/CacheManager.java b/src/main/java/org/traccar/session/cache/CacheManager.java
new file mode 100644
index 000000000..9d2350012
--- /dev/null
+++ b/src/main/java/org/traccar/session/cache/CacheManager.java
@@ -0,0 +1,425 @@
+/*
+ * 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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.broadcast.BroadcastInterface;
+import org.traccar.broadcast.BroadcastService;
+import org.traccar.config.Config;
+import org.traccar.model.Attribute;
+import org.traccar.model.BaseModel;
+import org.traccar.model.Calendar;
+import org.traccar.model.Device;
+import org.traccar.model.Driver;
+import org.traccar.model.Geofence;
+import org.traccar.model.Group;
+import org.traccar.model.GroupedModel;
+import org.traccar.model.Maintenance;
+import org.traccar.model.Notification;
+import org.traccar.model.Position;
+import org.traccar.model.ScheduledModel;
+import org.traccar.model.Server;
+import org.traccar.model.User;
+import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Request;
+
+import 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.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.stream.Collectors;
+
+@Singleton
+public class CacheManager implements BroadcastInterface {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(CacheManager.class);
+ private static final int GROUP_DEPTH_LIMIT = 3;
+ private static final Collection<Class<? extends BaseModel>> CLASSES = Arrays.asList(
+ Attribute.class, Driver.class, Geofence.class, Maintenance.class, Notification.class);
+
+ private final Config config;
+ private final Storage storage;
+ private final BroadcastService broadcastService;
+
+ private final ReadWriteLock lock = new ReentrantReadWriteLock();
+
+ private final Map<CacheKey, CacheValue> deviceCache = new HashMap<>();
+ private final Map<Long, Integer> deviceReferences = new HashMap<>();
+ private final Map<Long, Map<Class<? extends BaseModel>, Set<Long>>> deviceLinks = new HashMap<>();
+ private final Map<Long, Position> devicePositions = new HashMap<>();
+
+ private Server server;
+ private final Map<Long, List<User>> notificationUsers = new HashMap<>();
+
+ @Inject
+ public CacheManager(Config config, Storage storage, BroadcastService broadcastService) throws StorageException {
+ this.config = config;
+ this.storage = storage;
+ this.broadcastService = broadcastService;
+ invalidateServer();
+ invalidateUsers();
+ broadcastService.registerListener(this);
+ }
+
+ public Config getConfig() {
+ return config;
+ }
+
+ public <T extends BaseModel> T getObject(Class<T> clazz, long id) {
+ try {
+ lock.readLock().lock();
+ var cacheValue = deviceCache.get(new CacheKey(clazz, id));
+ return cacheValue != null ? cacheValue.getValue() : null;
+ } finally {
+ lock.readLock().unlock();
+ }
+ }
+
+ public <T extends BaseModel> List<T> getDeviceObjects(long deviceId, Class<T> clazz) {
+ try {
+ lock.readLock().lock();
+ var links = deviceLinks.get(deviceId);
+ if (links != null) {
+ return links.getOrDefault(clazz, new LinkedHashSet<>()).stream()
+ .map(id -> {
+ var cacheValue = deviceCache.get(new CacheKey(clazz, id));
+ return cacheValue != null ? cacheValue.<T>getValue() : null;
+ })
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ } else {
+ LOGGER.warn("Device {} cache missing", deviceId);
+ return Collections.emptyList();
+ }
+ } finally {
+ lock.readLock().unlock();
+ }
+ }
+
+ public Position getPosition(long deviceId) {
+ try {
+ lock.readLock().lock();
+ return devicePositions.get(deviceId);
+ } finally {
+ lock.readLock().unlock();
+ }
+ }
+
+ public Server getServer() {
+ try {
+ lock.readLock().lock();
+ return server;
+ } finally {
+ lock.readLock().unlock();
+ }
+ }
+
+ public List<User> getNotificationUsers(long notificationId, long deviceId) {
+ try {
+ lock.readLock().lock();
+ var users = deviceLinks.get(deviceId).get(User.class).stream()
+ .collect(Collectors.toUnmodifiableSet());
+ return notificationUsers.getOrDefault(notificationId, new LinkedList<>()).stream()
+ .filter(user -> users.contains(user.getId()))
+ .collect(Collectors.toUnmodifiableList());
+ } finally {
+ lock.readLock().unlock();
+ }
+ }
+
+ public Driver findDriverByUniqueId(long deviceId, String driverUniqueId) {
+ return getDeviceObjects(deviceId, Driver.class).stream()
+ .filter(driver -> driver.getUniqueId().equals(driverUniqueId))
+ .findFirst()
+ .orElse(null);
+ }
+
+ public void addDevice(long deviceId) throws StorageException {
+ try {
+ lock.writeLock().lock();
+ Integer references = deviceReferences.get(deviceId);
+ if (references != null) {
+ references += 1;
+ } else {
+ unsafeAddDevice(deviceId);
+ references = 1;
+ }
+ deviceReferences.put(deviceId, references);
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ public void removeDevice(long deviceId) {
+ try {
+ lock.writeLock().lock();
+ Integer references = deviceReferences.get(deviceId);
+ if (references != null) {
+ references -= 1;
+ if (references <= 0) {
+ unsafeRemoveDevice(deviceId);
+ deviceReferences.remove(deviceId);
+ } else {
+ deviceReferences.put(deviceId, references);
+ }
+ }
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ public void updatePosition(Position position) {
+ try {
+ lock.writeLock().lock();
+ if (deviceLinks.containsKey(position.getDeviceId())) {
+ devicePositions.put(position.getDeviceId(), position);
+ }
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ @Override
+ public void invalidateObject(boolean local, Class<? extends BaseModel> clazz, long id) {
+ try {
+ var object = storage.getObject(clazz, new Request(
+ new Columns.All(), new Condition.Equals("id", id)));
+ if (object != null) {
+ updateOrInvalidate(local, object);
+ } else {
+ invalidate(clazz, id);
+ }
+ } catch (StorageException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public <T extends BaseModel> void updateOrInvalidate(boolean local, T object) throws StorageException {
+ if (local) {
+ broadcastService.invalidateObject(true, object.getClass(), object.getId());
+ }
+
+ if (object instanceof Server) {
+ invalidateServer();
+ return;
+ }
+ if (object instanceof User) {
+ invalidateUsers();
+ return;
+ }
+
+ boolean invalidate = false;
+ var before = getObject(object.getClass(), object.getId());
+ if (before == null) {
+ return;
+ } else if (object instanceof GroupedModel) {
+ if (((GroupedModel) before).getGroupId() != ((GroupedModel) object).getGroupId()) {
+ invalidate = true;
+ }
+ } else if (object instanceof 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();
+ }
+ }
+ }
+
+ public <T extends BaseModel> void invalidate(Class<T> clazz, long id) throws StorageException {
+ invalidate(new CacheKey(clazz, id));
+ }
+
+ @Override
+ public void invalidatePermission(
+ boolean local,
+ Class<? extends BaseModel> clazz1, long id1,
+ Class<? extends BaseModel> clazz2, long id2) {
+ if (local) {
+ broadcastService.invalidatePermission(true, clazz1, id1, clazz2, id2);
+ }
+
+ try {
+ invalidate(new CacheKey(clazz1, id1), new CacheKey(clazz2, id2));
+ } catch (StorageException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void invalidateServer() throws StorageException {
+ server = storage.getObject(Server.class, new Request(new Columns.All()));
+ }
+
+ private void invalidateUsers() throws StorageException {
+ notificationUsers.clear();
+ Map<Long, User> users = new HashMap<>();
+ storage.getObjects(User.class, new Request(new Columns.All()))
+ .forEach(user -> users.put(user.getId(), user));
+ storage.getPermissions(User.class, Notification.class).forEach(permission -> {
+ long notificationId = permission.getPropertyId();
+ var user = users.get(permission.getOwnerId());
+ notificationUsers.computeIfAbsent(notificationId, k -> new LinkedList<>()).add(user);
+ });
+ }
+
+ private void addObject(long deviceId, BaseModel object) {
+ deviceCache.computeIfAbsent(new CacheKey(object), k -> new CacheValue(object)).retain(deviceId);
+ }
+
+ private void unsafeAddDevice(long deviceId) throws StorageException {
+ Map<Class<? extends BaseModel>, Set<Long>> links = new HashMap<>();
+
+ Device device = storage.getObject(Device.class, new Request(
+ new Columns.All(), new Condition.Equals("id", deviceId)));
+ if (device != null) {
+ addObject(deviceId, device);
+
+ int groupDepth = 0;
+ long groupId = device.getGroupId();
+ while (groupDepth < GROUP_DEPTH_LIMIT && groupId > 0) {
+ Group group = storage.getObject(Group.class, new Request(
+ new Columns.All(), new Condition.Equals("id", groupId)));
+ links.computeIfAbsent(Group.class, k -> new LinkedHashSet<>()).add(group.getId());
+ addObject(deviceId, group);
+ groupId = group.getGroupId();
+ groupDepth += 1;
+ }
+
+ for (Class<? extends BaseModel> clazz : CLASSES) {
+ var objects = storage.getObjects(clazz, new Request(
+ new Columns.All(), new Condition.Permission(Device.class, deviceId, clazz)));
+ links.put(clazz, objects.stream().map(BaseModel::getId).collect(Collectors.toSet()));
+ for (var object : objects) {
+ addObject(deviceId, object);
+ if (object instanceof 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);
+ }
+ }
+ }
+ }
+
+ 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);
+ }
+ }
+ }
+
+ deviceLinks.put(deviceId, links);
+
+ if (device.getPositionId() > 0) {
+ devicePositions.put(deviceId, storage.getObject(Position.class, new Request(
+ new Columns.All(), new Condition.Equals("id", device.getPositionId()))));
+ }
+ }
+ }
+
+ private void unsafeRemoveDevice(long deviceId) {
+ deviceCache.remove(new CacheKey(Device.class, deviceId));
+ deviceLinks.remove(deviceId).forEach((clazz, ids) -> ids.forEach(id -> {
+ var key = new CacheKey(clazz, id);
+ deviceCache.computeIfPresent(key, (k, value) -> {
+ value.release(deviceId);
+ return value.getReferences().size() > 0 ? value : null;
+ });
+ }));
+ devicePositions.remove(deviceId);
+ }
+
+ private void invalidate(CacheKey... keys) throws StorageException {
+ try {
+ lock.writeLock().lock();
+ unsafeInvalidate(keys);
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ private void unsafeInvalidate(CacheKey[] keys) throws StorageException {
+ boolean invalidateServer = false;
+ boolean invalidateUsers = false;
+ Set<Long> linkedDevices = new HashSet<>();
+ for (var key : keys) {
+ if (key.classIs(Server.class)) {
+ invalidateServer = true;
+ } else {
+ if (key.classIs(User.class) || key.classIs(Notification.class)) {
+ invalidateUsers = true;
+ }
+ deviceCache.computeIfPresent(key, (k, value) -> {
+ linkedDevices.addAll(value.getReferences());
+ return value;
+ });
+ }
+ }
+ for (long deviceId : linkedDevices) {
+ unsafeRemoveDevice(deviceId);
+ unsafeAddDevice(deviceId);
+ }
+ if (invalidateServer) {
+ invalidateServer();
+ }
+ if (invalidateUsers) {
+ invalidateUsers();
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/session/cache/CacheValue.java b/src/main/java/org/traccar/session/cache/CacheValue.java
new file mode 100644
index 000000000..1f0383ce5
--- /dev/null
+++ b/src/main/java/org/traccar/session/cache/CacheValue.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.session.cache;
+
+import org.traccar.model.BaseModel;
+
+import java.util.HashSet;
+import java.util.Set;
+
+class CacheValue {
+
+ private BaseModel value;
+ private final Set<Long> references = new HashSet<>();
+
+ CacheValue(BaseModel value) {
+ this.value = value;
+ }
+
+ public void retain(long deviceId) {
+ references.add(deviceId);
+ }
+
+ public void release(long deviceId) {
+ references.remove(deviceId);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T extends BaseModel> T getValue() {
+ return (T) value;
+ }
+
+ public void setValue(BaseModel value) {
+ this.value = value;
+ }
+
+ public Set<Long> getReferences() {
+ return references;
+ }
+
+}
diff --git a/src/main/java/org/traccar/session/state/MotionProcessor.java b/src/main/java/org/traccar/session/state/MotionProcessor.java
new file mode 100644
index 000000000..a1737a739
--- /dev/null
+++ b/src/main/java/org/traccar/session/state/MotionProcessor.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.session.state;
+
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+import org.traccar.reports.common.TripsConfig;
+
+public final class MotionProcessor {
+
+ private MotionProcessor() {
+ }
+
+ public static void updateState(
+ MotionState state, Position position, boolean newState, TripsConfig tripsConfig) {
+
+ state.setEvent(null);
+
+ boolean oldState = state.getMotionState();
+ if (oldState == newState) {
+ if (state.getMotionTime() != null) {
+ long oldTime = state.getMotionTime().getTime();
+ long newTime = position.getFixTime().getTime();
+
+ double distance = position.getDouble(Position.KEY_TOTAL_DISTANCE) - state.getMotionDistance();
+ Boolean ignition = null;
+ if (tripsConfig.getUseIgnition() && position.hasAttribute(Position.KEY_IGNITION)) {
+ ignition = position.getBoolean(Position.KEY_IGNITION);
+ }
+
+ boolean generateEvent = false;
+ if (newState) {
+ if (newTime - oldTime >= tripsConfig.getMinimalTripDuration()
+ || distance >= tripsConfig.getMinimalTripDistance()) {
+ generateEvent = true;
+ }
+ } else {
+ if (newTime - oldTime >= tripsConfig.getMinimalParkingDuration()
+ || ignition != null && !ignition) {
+ generateEvent = true;
+ }
+ }
+
+ if (generateEvent) {
+
+ String eventType = newState ? Event.TYPE_DEVICE_MOVING : Event.TYPE_DEVICE_STOPPED;
+ Event event = new Event(eventType, position);
+
+ state.setMotionStreak(newState);
+ state.setMotionTime(null);
+ state.setMotionDistance(0);
+ state.setEvent(event);
+
+ }
+ }
+ } else {
+ state.setMotionState(newState);
+ if (state.getMotionStreak() == newState) {
+ state.setMotionTime(null);
+ state.setMotionDistance(0);
+ } else {
+ state.setMotionTime(position.getFixTime());
+ state.setMotionDistance(position.getDouble(Position.KEY_TOTAL_DISTANCE));
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/session/state/MotionState.java b/src/main/java/org/traccar/session/state/MotionState.java
new file mode 100644
index 000000000..6c917ad16
--- /dev/null
+++ b/src/main/java/org/traccar/session/state/MotionState.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.session.state;
+
+import org.traccar.model.Device;
+import org.traccar.model.Event;
+
+import java.util.Date;
+
+public class MotionState {
+
+ public static MotionState fromDevice(Device device) {
+ MotionState state = new MotionState();
+ state.motionStreak = device.getMotionStreak();
+ state.motionState = device.getMotionState();
+ state.motionTime = device.getMotionTime();
+ state.motionDistance = device.getMotionDistance();
+ return state;
+ }
+
+ public void toDevice(Device device) {
+ device.setMotionStreak(motionStreak);
+ device.setMotionState(motionState);
+ device.setMotionTime(motionTime);
+ device.setMotionDistance(motionDistance);
+ }
+
+ private boolean changed;
+
+ public boolean isChanged() {
+ return changed;
+ }
+
+ private boolean motionStreak;
+
+ public boolean getMotionStreak() {
+ return motionStreak;
+ }
+
+ public void setMotionStreak(boolean motionStreak) {
+ this.motionStreak = motionStreak;
+ changed = true;
+ }
+
+ private boolean motionState;
+
+ public boolean getMotionState() {
+ return motionState;
+ }
+
+ public void setMotionState(boolean motionState) {
+ this.motionState = motionState;
+ changed = true;
+ }
+
+ private Date motionTime;
+
+ public Date getMotionTime() {
+ return motionTime;
+ }
+
+ public void setMotionTime(Date motionTime) {
+ this.motionTime = motionTime;
+ changed = true;
+ }
+
+ private double motionDistance;
+
+ public double getMotionDistance() {
+ return motionDistance;
+ }
+
+ public void setMotionDistance(double motionDistance) {
+ this.motionDistance = motionDistance;
+ changed = true;
+ }
+
+ private Event event;
+
+ public Event getEvent() {
+ return event;
+ }
+
+ public void setEvent(Event event) {
+ this.event = event;
+ }
+
+}
diff --git a/src/main/java/org/traccar/session/state/OverspeedProcessor.java b/src/main/java/org/traccar/session/state/OverspeedProcessor.java
new file mode 100644
index 000000000..62f6a3de2
--- /dev/null
+++ b/src/main/java/org/traccar/session/state/OverspeedProcessor.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.session.state;
+
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+
+public final class OverspeedProcessor {
+
+ public static final String ATTRIBUTE_SPEED = "speed";
+
+ private OverspeedProcessor() {
+ }
+
+ public static void updateState(
+ OverspeedState state, Position position, double speedLimit, long minimalDuration, long geofenceId) {
+
+ state.setEvent(null);
+
+ boolean oldState = state.getOverspeedState();
+ if (oldState) {
+ boolean newState = position.getSpeed() > speedLimit;
+ 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);
+
+ }
+ }
+ } else {
+ state.setOverspeedState(false);
+ state.setOverspeedTime(null);
+ state.setOverspeedGeofenceId(0);
+ }
+ } else if (position != null && position.getSpeed() > speedLimit) {
+ state.setOverspeedState(true);
+ state.setOverspeedTime(position.getFixTime());
+ state.setOverspeedGeofenceId(geofenceId);
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/session/state/OverspeedState.java b/src/main/java/org/traccar/session/state/OverspeedState.java
new file mode 100644
index 000000000..340ede6d7
--- /dev/null
+++ b/src/main/java/org/traccar/session/state/OverspeedState.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.session.state;
+
+import org.traccar.model.Device;
+import org.traccar.model.Event;
+
+import java.util.Date;
+
+public class OverspeedState {
+
+ public static OverspeedState fromDevice(Device device) {
+ OverspeedState state = new OverspeedState();
+ state.overspeedState = device.getOverspeedState();
+ state.overspeedTime = device.getOverspeedTime();
+ state.overspeedGeofenceId = device.getOverspeedGeofenceId();
+ return state;
+ }
+
+ public void toDevice(Device device) {
+ device.setOverspeedState(overspeedState);
+ device.setOverspeedTime(overspeedTime);
+ device.setOverspeedGeofenceId(overspeedGeofenceId);
+ }
+
+ private boolean changed;
+
+ public boolean isChanged() {
+ return changed;
+ }
+
+ private boolean overspeedState;
+
+ public boolean getOverspeedState() {
+ return overspeedState;
+ }
+
+ public void setOverspeedState(boolean overspeedState) {
+ this.overspeedState = overspeedState;
+ changed = true;
+ }
+
+ private Date overspeedTime;
+
+ public Date getOverspeedTime() {
+ return overspeedTime;
+ }
+
+ public void setOverspeedTime(Date overspeedTime) {
+ this.overspeedTime = overspeedTime;
+ changed = true;
+ }
+
+ private long overspeedGeofenceId;
+
+ public long getOverspeedGeofenceId() {
+ return overspeedGeofenceId;
+ }
+
+ public void setOverspeedGeofenceId(long overspeedGeofenceId) {
+ this.overspeedGeofenceId = overspeedGeofenceId;
+ changed = true;
+ }
+
+ private Event event;
+
+ public Event getEvent() {
+ return event;
+ }
+
+ public void setEvent(Event event) {
+ this.event = event;
+ }
+
+}
diff --git a/src/main/java/org/traccar/sms/HttpSmsClient.java b/src/main/java/org/traccar/sms/HttpSmsClient.java
index 6234eabb8..51b161594 100644
--- a/src/main/java/org/traccar/sms/HttpSmsClient.java
+++ b/src/main/java/org/traccar/sms/HttpSmsClient.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,16 +16,14 @@
*/
package org.traccar.sms;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.traccar.Context;
+import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.helper.DataConverter;
import org.traccar.notification.MessageException;
+import javax.ws.rs.client.Client;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
-import javax.ws.rs.client.InvocationCallback;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.UnsupportedEncodingException;
@@ -34,8 +32,7 @@ import java.nio.charset.StandardCharsets;
public class HttpSmsClient implements SmsManager {
- private static final Logger LOGGER = LoggerFactory.getLogger(HttpSmsClient.class);
-
+ private final Client client;
private final String url;
private final String authorizationHeader;
private final String authorization;
@@ -43,14 +40,15 @@ public class HttpSmsClient implements SmsManager {
private final boolean encode;
private final MediaType mediaType;
- public HttpSmsClient() {
- url = Context.getConfig().getString(Keys.SMS_HTTP_URL);
- authorizationHeader = Context.getConfig().getString(Keys.SMS_HTTP_AUTHORIZATION_HEADER);
- if (Context.getConfig().hasKey(Keys.SMS_HTTP_AUTHORIZATION)) {
- authorization = Context.getConfig().getString(Keys.SMS_HTTP_AUTHORIZATION);
+ public HttpSmsClient(Config config, Client client) {
+ this.client = client;
+ url = config.getString(Keys.SMS_HTTP_URL);
+ authorizationHeader = config.getString(Keys.SMS_HTTP_AUTHORIZATION_HEADER);
+ if (config.hasKey(Keys.SMS_HTTP_AUTHORIZATION)) {
+ authorization = config.getString(Keys.SMS_HTTP_AUTHORIZATION);
} else {
- String user = Context.getConfig().getString(Keys.SMS_HTTP_USER);
- String password = Context.getConfig().getString(Keys.SMS_HTTP_PASSWORD);
+ String user = config.getString(Keys.SMS_HTTP_USER);
+ String password = config.getString(Keys.SMS_HTTP_PASSWORD);
if (user != null && password != null) {
authorization = "Basic "
+ DataConverter.printBase64((user + ":" + password).getBytes(StandardCharsets.UTF_8));
@@ -58,7 +56,7 @@ public class HttpSmsClient implements SmsManager {
authorization = null;
}
}
- template = Context.getConfig().getString(Keys.SMS_HTTP_TEMPLATE).trim();
+ template = config.getString(Keys.SMS_HTTP_TEMPLATE).trim();
if (template.charAt(0) == '{' || template.charAt(0) == '[') {
encode = false;
mediaType = MediaType.APPLICATION_JSON_TYPE;
@@ -83,7 +81,7 @@ public class HttpSmsClient implements SmsManager {
}
private Invocation.Builder getRequestBuilder() {
- Invocation.Builder builder = Context.getClient().target(url).request();
+ Invocation.Builder builder = client.target(url).request();
if (authorization != null) {
builder = builder.header(authorizationHeader, authorization);
}
@@ -91,26 +89,13 @@ public class HttpSmsClient implements SmsManager {
}
@Override
- public void sendMessageSync(String destAddress, String message, boolean command) throws MessageException {
- Response response = getRequestBuilder().post(Entity.entity(preparePayload(destAddress, message), mediaType));
- if (response.getStatus() / 100 != 2) {
- throw new MessageException(response.readEntity(String.class));
- }
- }
-
- @Override
- public void sendMessageAsync(final String destAddress, final String message, final boolean command) {
- getRequestBuilder().async().post(
- Entity.entity(preparePayload(destAddress, message), mediaType), new InvocationCallback<String>() {
- @Override
- public void completed(String s) {
- }
-
- @Override
- public void failed(Throwable throwable) {
- LOGGER.warn("SMS send failed", throwable);
+ public void sendMessage(String destAddress, String message, boolean command) throws MessageException {
+ try (Response response = getRequestBuilder()
+ .post(Entity.entity(preparePayload(destAddress, message), mediaType))) {
+ if (response.getStatus() / 100 != 2) {
+ throw new MessageException(response.readEntity(String.class));
}
- });
+ }
}
}
diff --git a/src/main/java/org/traccar/sms/SmsManager.java b/src/main/java/org/traccar/sms/SmsManager.java
index 3b0cbda7f..9ab25d9cb 100644
--- a/src/main/java/org/traccar/sms/SmsManager.java
+++ b/src/main/java/org/traccar/sms/SmsManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,10 +20,7 @@ import org.traccar.notification.MessageException;
public interface SmsManager {
- void sendMessageSync(
+ void sendMessage(
String destAddress, String message, boolean command) throws InterruptedException, MessageException;
- void sendMessageAsync(
- String destAddress, String message, boolean command);
-
}
diff --git a/src/main/java/org/traccar/sms/SnsSmsClient.java b/src/main/java/org/traccar/sms/SnsSmsClient.java
index bdd4104f5..ed5a325cc 100644
--- a/src/main/java/org/traccar/sms/SnsSmsClient.java
+++ b/src/main/java/org/traccar/sms/SnsSmsClient.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2021 Subodh Ranadive (subodhranadive3103@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,7 +27,7 @@ import com.amazonaws.services.sns.model.PublishRequest;
import com.amazonaws.services.sns.model.PublishResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.traccar.Context;
+import org.traccar.config.Config;
import org.traccar.config.Keys;
import java.util.HashMap;
@@ -38,23 +38,16 @@ public class SnsSmsClient implements SmsManager {
private final AmazonSNSAsync snsClient;
- public SnsSmsClient() {
- if (Context.getConfig().hasKey(Keys.SMS_AWS_REGION)
- && Context.getConfig().hasKey(Keys.SMS_AWS_ACCESS)
- && Context.getConfig().hasKey(Keys.SMS_AWS_SECRET)) {
- BasicAWSCredentials awsCredentials =
- new BasicAWSCredentials(Context.getConfig().getString(Keys.SMS_AWS_ACCESS),
- Context.getConfig().getString(Keys.SMS_AWS_SECRET));
- snsClient = AmazonSNSAsyncClientBuilder.standard()
- .withRegion(Context.getConfig().getString(Keys.SMS_AWS_REGION))
- .withCredentials(new AWSStaticCredentialsProvider(awsCredentials)).build();
- } else {
- throw new RuntimeException("SNS Not Configured Properly. Please provide valid config.");
- }
+ public SnsSmsClient(Config config) {
+ BasicAWSCredentials awsCredentials = new BasicAWSCredentials(
+ config.getString(Keys.SMS_AWS_ACCESS), config.getString(Keys.SMS_AWS_SECRET));
+ snsClient = AmazonSNSAsyncClientBuilder.standard()
+ .withRegion(config.getString(Keys.SMS_AWS_REGION))
+ .withCredentials(new AWSStaticCredentialsProvider(awsCredentials)).build();
}
@Override
- public void sendMessageSync(String destAddress, String message, boolean command) {
+ public void sendMessage(String destAddress, String message, boolean command) {
Map<String, MessageAttributeValue> smsAttributes = new HashMap<>();
smsAttributes.put("AWS.SNS.SMS.SenderID",
new MessageAttributeValue().withStringValue("SNS").withDataType("String"));
@@ -64,19 +57,15 @@ public class SnsSmsClient implements SmsManager {
PublishRequest publishRequest = new PublishRequest().withMessage(message)
.withPhoneNumber(destAddress).withMessageAttributes(smsAttributes);
- snsClient.publishAsync(publishRequest, new AsyncHandler<PublishRequest, PublishResult>() {
+ snsClient.publishAsync(publishRequest, new AsyncHandler<>() {
@Override
public void onError(Exception exception) {
LOGGER.error("SMS send failed", exception);
}
+
@Override
public void onSuccess(PublishRequest request, PublishResult result) {
}
});
}
-
- @Override
- public void sendMessageAsync(String destAddress, String message, boolean command) {
- sendMessageSync(destAddress, message, command);
- }
}
diff --git a/src/main/java/org/traccar/speedlimit/OverpassSpeedLimitProvider.java b/src/main/java/org/traccar/speedlimit/OverpassSpeedLimitProvider.java
index 429a47c76..edf089f37 100644
--- a/src/main/java/org/traccar/speedlimit/OverpassSpeedLimitProvider.java
+++ b/src/main/java/org/traccar/speedlimit/OverpassSpeedLimitProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2020 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,19 +15,21 @@
*/
package org.traccar.speedlimit;
-import org.traccar.Context;
import org.traccar.helper.UnitsConverter;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.ws.rs.client.AsyncInvoker;
+import javax.ws.rs.client.Client;
import javax.ws.rs.client.InvocationCallback;
public class OverpassSpeedLimitProvider implements SpeedLimitProvider {
+ private final Client client;
private final String url;
- public OverpassSpeedLimitProvider(String url) {
+ public OverpassSpeedLimitProvider(Client client, String url) {
+ this.client = client;
this.url = url + "?data=[out:json];way[maxspeed](around:100.0,%f,%f);out%%20tags;";
}
@@ -46,7 +48,7 @@ public class OverpassSpeedLimitProvider implements SpeedLimitProvider {
@Override
public void getSpeedLimit(double latitude, double longitude, SpeedLimitProviderCallback callback) {
String formattedUrl = String.format(url, latitude, longitude);
- AsyncInvoker invoker = Context.getClient().target(formattedUrl).request().async();
+ AsyncInvoker invoker = client.target(formattedUrl).request().async();
invoker.get(new InvocationCallback<JsonObject>() {
@Override
public void completed(JsonObject json) {
diff --git a/src/main/java/org/traccar/storage/DatabaseModule.java b/src/main/java/org/traccar/storage/DatabaseModule.java
new file mode 100644
index 000000000..3e3483818
--- /dev/null
+++ b/src/main/java/org/traccar/storage/DatabaseModule.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.storage;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import liquibase.Contexts;
+import liquibase.Liquibase;
+import liquibase.database.Database;
+import liquibase.database.DatabaseFactory;
+import liquibase.exception.LiquibaseException;
+import liquibase.resource.DirectoryResourceAccessor;
+import liquibase.resource.ResourceAccessor;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+
+import javax.inject.Singleton;
+import javax.sql.DataSource;
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.net.URL;
+
+public class DatabaseModule extends AbstractModule {
+
+ @Singleton
+ @Provides
+ public static DataSource provideDataSource(
+ Config config) throws ReflectiveOperationException, IOException, LiquibaseException {
+
+ String driverFile = config.getString(Keys.DATABASE_DRIVER_FILE);
+ if (driverFile != null) {
+ ClassLoader classLoader = ClassLoader.getSystemClassLoader();
+ try {
+ Method method = classLoader.getClass().getDeclaredMethod("addURL", URL.class);
+ method.setAccessible(true);
+ method.invoke(classLoader, new File(driverFile).toURI().toURL());
+ } catch (NoSuchMethodException e) {
+ Method method = classLoader.getClass()
+ .getDeclaredMethod("appendToClassPathForInstrumentation", String.class);
+ method.setAccessible(true);
+ method.invoke(classLoader, driverFile);
+ }
+ }
+
+ String driver = config.getString(Keys.DATABASE_DRIVER);
+ if (driver != null) {
+ Class.forName(driver);
+ }
+
+ HikariConfig hikariConfig = new HikariConfig();
+ hikariConfig.setDriverClassName(driver);
+ hikariConfig.setJdbcUrl(config.getString(Keys.DATABASE_URL));
+ hikariConfig.setUsername(config.getString(Keys.DATABASE_USER));
+ hikariConfig.setPassword(config.getString(Keys.DATABASE_PASSWORD));
+ hikariConfig.setConnectionInitSql(config.getString(Keys.DATABASE_CHECK_CONNECTION));
+ hikariConfig.setIdleTimeout(600000);
+
+ int maxPoolSize = config.getInteger(Keys.DATABASE_MAX_POOL_SIZE);
+ if (maxPoolSize != 0) {
+ hikariConfig.setMaximumPoolSize(maxPoolSize);
+ }
+
+ DataSource dataSource = new HikariDataSource(hikariConfig);
+
+ if (config.hasKey(Keys.DATABASE_CHANGELOG)) {
+
+ ResourceAccessor resourceAccessor = new DirectoryResourceAccessor(new File("."));
+
+ Database database = DatabaseFactory.getInstance().openDatabase(
+ config.getString(Keys.DATABASE_URL),
+ config.getString(Keys.DATABASE_USER),
+ config.getString(Keys.DATABASE_PASSWORD),
+ config.getString(Keys.DATABASE_DRIVER),
+ null, null, null, resourceAccessor);
+
+ String changelog = config.getString(Keys.DATABASE_CHANGELOG);
+
+ try (Liquibase liquibase = new Liquibase(changelog, resourceAccessor, database)) {
+ liquibase.clearCheckSums();
+ liquibase.update(new Contexts());
+ }
+ }
+
+ return dataSource;
+ }
+
+}
diff --git a/src/main/java/org/traccar/storage/DatabaseStorage.java b/src/main/java/org/traccar/storage/DatabaseStorage.java
new file mode 100644
index 000000000..a049a641c
--- /dev/null
+++ b/src/main/java/org/traccar/storage/DatabaseStorage.java
@@ -0,0 +1,410 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.storage;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.traccar.config.Config;
+import org.traccar.model.BaseModel;
+import org.traccar.model.Device;
+import org.traccar.model.Group;
+import org.traccar.model.GroupedModel;
+import org.traccar.model.Permission;
+import org.traccar.storage.query.Columns;
+import org.traccar.storage.query.Condition;
+import org.traccar.storage.query.Order;
+import org.traccar.storage.query.Request;
+
+import javax.inject.Inject;
+import javax.sql.DataSource;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+public class DatabaseStorage extends Storage {
+
+ private final Config config;
+ private final DataSource dataSource;
+ private final ObjectMapper objectMapper;
+ private final String databaseType;
+
+ @Inject
+ public DatabaseStorage(Config config, DataSource dataSource, ObjectMapper objectMapper) {
+ this.config = config;
+ this.dataSource = dataSource;
+ this.objectMapper = objectMapper;
+
+ try {
+ databaseType = dataSource.getConnection().getMetaData().getDatabaseProductName();
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public <T> List<T> getObjects(Class<T> clazz, Request request) throws StorageException {
+ StringBuilder query = new StringBuilder("SELECT ");
+ if (request.getColumns() instanceof Columns.All) {
+ query.append('*');
+ } else {
+ query.append(formatColumns(request.getColumns().getColumns(clazz, "set"), c -> c));
+ }
+ query.append(" FROM ").append(getStorageName(clazz));
+ query.append(formatCondition(request.getCondition()));
+ query.append(formatOrder(request.getOrder()));
+ try {
+ QueryBuilder builder = QueryBuilder.create(config, dataSource, objectMapper, query.toString());
+ for (Map.Entry<String, Object> variable : getConditionVariables(request.getCondition()).entrySet()) {
+ builder.setValue(variable.getKey(), variable.getValue());
+ }
+ return builder.executeQuery(clazz);
+ } catch (SQLException e) {
+ throw new StorageException(e);
+ }
+ }
+
+ @Override
+ public <T> long addObject(T entity, Request request) throws StorageException {
+ List<String> columns = request.getColumns().getColumns(entity.getClass(), "get");
+ StringBuilder query = new StringBuilder("INSERT INTO ");
+ query.append(getStorageName(entity.getClass()));
+ query.append("(");
+ query.append(formatColumns(columns, c -> c));
+ query.append(") VALUES (");
+ query.append(formatColumns(columns, c -> ':' + c));
+ query.append(")");
+ try {
+ QueryBuilder builder = QueryBuilder.create(config, dataSource, objectMapper, query.toString(), true);
+ builder.setObject(entity, columns);
+ return builder.executeUpdate();
+ } catch (SQLException e) {
+ throw new StorageException(e);
+ }
+ }
+
+ @Override
+ public <T> void updateObject(T entity, Request request) throws StorageException {
+ List<String> columns = request.getColumns().getColumns(entity.getClass(), "get");
+ StringBuilder query = new StringBuilder("UPDATE ");
+ query.append(getStorageName(entity.getClass()));
+ query.append(" SET ");
+ query.append(formatColumns(columns, c -> c + " = :" + c));
+ query.append(formatCondition(request.getCondition()));
+ try {
+ QueryBuilder builder = QueryBuilder.create(config, dataSource, objectMapper, query.toString());
+ builder.setObject(entity, columns);
+ for (Map.Entry<String, Object> variable : getConditionVariables(request.getCondition()).entrySet()) {
+ builder.setValue(variable.getKey(), variable.getValue());
+ }
+ builder.executeUpdate();
+ } catch (SQLException e) {
+ throw new StorageException(e);
+ }
+ }
+
+ @Override
+ public void removeObject(Class<?> clazz, Request request) throws StorageException {
+ StringBuilder query = new StringBuilder("DELETE FROM ");
+ query.append(getStorageName(clazz));
+ query.append(formatCondition(request.getCondition()));
+ try {
+ QueryBuilder builder = QueryBuilder.create(config, dataSource, objectMapper, query.toString());
+ for (Map.Entry<String, Object> variable : getConditionVariables(request.getCondition()).entrySet()) {
+ builder.setValue(variable.getKey(), variable.getValue());
+ }
+ builder.executeUpdate();
+ } catch (SQLException e) {
+ throw new StorageException(e);
+ }
+ }
+
+ @Override
+ public List<Permission> getPermissions(
+ Class<? extends BaseModel> ownerClass, long ownerId,
+ Class<? extends BaseModel> propertyClass, long propertyId) throws StorageException {
+ StringBuilder query = new StringBuilder("SELECT * FROM ");
+ query.append(Permission.getStorageName(ownerClass, propertyClass));
+ var conditions = new LinkedList<Condition>();
+ if (ownerId > 0) {
+ conditions.add(new Condition.Equals(Permission.getKey(ownerClass), ownerId));
+ }
+ if (propertyId > 0) {
+ conditions.add(new Condition.Equals(Permission.getKey(propertyClass), propertyId));
+ }
+ Condition combinedCondition = Condition.merge(conditions);
+ query.append(formatCondition(combinedCondition));
+ try {
+ QueryBuilder builder = QueryBuilder.create(config, dataSource, objectMapper, query.toString());
+ for (Map.Entry<String, Object> variable : getConditionVariables(combinedCondition).entrySet()) {
+ builder.setValue(variable.getKey(), variable.getValue());
+ }
+ return builder.executePermissionsQuery();
+ } catch (SQLException e) {
+ throw new StorageException(e);
+ }
+ }
+
+ @Override
+ public void addPermission(Permission permission) throws StorageException {
+ StringBuilder query = new StringBuilder("INSERT INTO ");
+ query.append(permission.getStorageName());
+ query.append(" VALUES (");
+ query.append(permission.get().keySet().stream().map(key -> ':' + key).collect(Collectors.joining(", ")));
+ query.append(")");
+ try {
+ QueryBuilder builder = QueryBuilder.create(config, dataSource, objectMapper, query.toString(), true);
+ for (var entry : permission.get().entrySet()) {
+ builder.setLong(entry.getKey(), entry.getValue());
+ }
+ builder.executeUpdate();
+ } catch (SQLException e) {
+ throw new StorageException(e);
+ }
+ }
+
+ @Override
+ public void removePermission(Permission permission) throws StorageException {
+ StringBuilder query = new StringBuilder("DELETE FROM ");
+ query.append(permission.getStorageName());
+ query.append(" WHERE ");
+ query.append(permission
+ .get().keySet().stream().map(key -> key + " = :" + key).collect(Collectors.joining(" AND ")));
+ try {
+ QueryBuilder builder = QueryBuilder.create(config, dataSource, objectMapper, query.toString(), true);
+ for (var entry : permission.get().entrySet()) {
+ builder.setLong(entry.getKey(), entry.getValue());
+ }
+ builder.executeUpdate();
+ } catch (SQLException e) {
+ throw new StorageException(e);
+ }
+ }
+
+ private String getStorageName(Class<?> clazz) throws StorageException {
+ StorageName storageName = clazz.getAnnotation(StorageName.class);
+ if (storageName == null) {
+ throw new StorageException("StorageName annotation is missing");
+ }
+ return storageName.value();
+ }
+
+ private Map<String, Object> getConditionVariables(Condition genericCondition) {
+ Map<String, Object> results = new HashMap<>();
+ if (genericCondition instanceof Condition.Compare) {
+ var condition = (Condition.Compare) genericCondition;
+ if (condition.getValue() != null) {
+ results.put(condition.getVariable(), condition.getValue());
+ }
+ } else if (genericCondition instanceof Condition.Between) {
+ var condition = (Condition.Between) genericCondition;
+ results.put(condition.getFromVariable(), condition.getFromValue());
+ results.put(condition.getToVariable(), condition.getToValue());
+ } else if (genericCondition instanceof Condition.Binary) {
+ var condition = (Condition.Binary) genericCondition;
+ results.putAll(getConditionVariables(condition.getFirst()));
+ results.putAll(getConditionVariables(condition.getSecond()));
+ } else if (genericCondition instanceof Condition.Permission) {
+ var condition = (Condition.Permission) genericCondition;
+ if (condition.getOwnerId() > 0) {
+ results.put(Permission.getKey(condition.getOwnerClass()), condition.getOwnerId());
+ } else {
+ results.put(Permission.getKey(condition.getPropertyClass()), condition.getPropertyId());
+ }
+ } else if (genericCondition instanceof Condition.LatestPositions) {
+ var condition = (Condition.LatestPositions) genericCondition;
+ if (condition.getDeviceId() > 0) {
+ results.put("deviceId", condition.getDeviceId());
+ }
+ }
+ return results;
+ }
+
+ private String formatColumns(List<String> columns, Function<String, String> mapper) {
+ return columns.stream().map(mapper).collect(Collectors.joining(", "));
+ }
+
+ private String formatCondition(Condition genericCondition) throws StorageException {
+ return formatCondition(genericCondition, true);
+ }
+
+ private String formatCondition(Condition genericCondition, boolean appendWhere) throws StorageException {
+ StringBuilder result = new StringBuilder();
+ if (genericCondition != null) {
+ if (appendWhere) {
+ result.append(" WHERE ");
+ }
+ if (genericCondition instanceof Condition.Compare) {
+
+ var condition = (Condition.Compare) genericCondition;
+ result.append(condition.getColumn());
+ result.append(" ");
+ result.append(condition.getOperator());
+ result.append(" :");
+ result.append(condition.getVariable());
+
+ } else if (genericCondition instanceof Condition.Between) {
+
+ var condition = (Condition.Between) genericCondition;
+ result.append(condition.getColumn());
+ result.append(" BETWEEN :");
+ result.append(condition.getFromVariable());
+ result.append(" AND :");
+ result.append(condition.getToVariable());
+
+ } else if (genericCondition instanceof Condition.Binary) {
+
+ var condition = (Condition.Binary) genericCondition;
+ result.append(formatCondition(condition.getFirst(), false));
+ result.append(" ");
+ result.append(condition.getOperator());
+ result.append(" ");
+ result.append(formatCondition(condition.getSecond(), false));
+
+ } else if (genericCondition instanceof Condition.Permission) {
+
+ var condition = (Condition.Permission) genericCondition;
+ result.append("id IN (");
+ result.append(formatPermissionQuery(condition));
+ result.append(")");
+
+ } else if (genericCondition instanceof Condition.LatestPositions) {
+
+ var condition = (Condition.LatestPositions) genericCondition;
+ result.append("id IN (");
+ result.append("SELECT positionId FROM ");
+ result.append(getStorageName(Device.class));
+ if (condition.getDeviceId() > 0) {
+ result.append(" WHERE id = :deviceId");
+ }
+ result.append(")");
+
+ }
+ }
+ return result.toString();
+ }
+
+ private String formatOrder(Order order) {
+ StringBuilder result = new StringBuilder();
+ if (order != null) {
+ result.append(" ORDER BY ");
+ result.append(order.getColumn());
+ if (order.getDescending()) {
+ result.append(" DESC");
+ }
+ if (order.getLimit() > 0) {
+ if (databaseType.equals("Microsoft SQL Server")) {
+ result.append(" OFFSET 0 ROWS FETCH FIRST ");
+ result.append(order.getLimit());
+ result.append(" ROWS ONLY");
+ } else {
+ result.append(" LIMIT ");
+ result.append(order.getLimit());
+ }
+ }
+ }
+ return result.toString();
+ }
+
+ private String formatPermissionQuery(Condition.Permission condition) throws StorageException {
+ StringBuilder result = new StringBuilder();
+
+ String outputKey;
+ String conditionKey;
+ if (condition.getOwnerId() > 0) {
+ outputKey = Permission.getKey(condition.getPropertyClass());
+ conditionKey = Permission.getKey(condition.getOwnerClass());
+ } else {
+ outputKey = Permission.getKey(condition.getOwnerClass());
+ conditionKey = Permission.getKey(condition.getPropertyClass());
+ }
+
+ String storageName = Permission.getStorageName(condition.getOwnerClass(), condition.getPropertyClass());
+ result.append("SELECT ");
+ result.append(storageName).append('.').append(outputKey);
+ result.append(" FROM ");
+ result.append(storageName);
+ result.append(" WHERE ");
+ result.append(conditionKey);
+ result.append(" = :");
+ result.append(conditionKey);
+
+ if (condition.getIncludeGroups()) {
+
+ boolean expandDevices;
+ String groupStorageName;
+ if (GroupedModel.class.isAssignableFrom(condition.getOwnerClass())) {
+ expandDevices = Device.class.isAssignableFrom(condition.getOwnerClass());
+ groupStorageName = Permission.getStorageName(Group.class, condition.getPropertyClass());
+ } else {
+ expandDevices = Device.class.isAssignableFrom(condition.getPropertyClass());
+ groupStorageName = Permission.getStorageName(condition.getOwnerClass(), Group.class);
+ }
+
+ result.append(" UNION ");
+
+ result.append("SELECT DISTINCT ");
+ if (!expandDevices) {
+ if (outputKey.equals("groupId")) {
+ result.append("all_groups.");
+ } else {
+ result.append(groupStorageName).append('.');
+ }
+ }
+ result.append(outputKey);
+ result.append(" FROM ");
+ result.append(groupStorageName);
+
+ result.append(" INNER JOIN (");
+ result.append("SELECT id as parentId, id as groupId FROM ");
+ result.append(getStorageName(Group.class));
+ result.append(" UNION ");
+ result.append("SELECT groupId as parentId, id as groupId FROM ");
+ result.append(getStorageName(Group.class));
+ result.append(" WHERE groupId IS NOT NULL");
+ result.append(" UNION ");
+ result.append("SELECT g2.groupId as parentId, g1.id as groupId FROM ");
+ result.append(getStorageName(Group.class));
+ result.append(" AS g2");
+ result.append(" INNER JOIN ");
+ result.append(getStorageName(Group.class));
+ result.append(" AS g1 ON g2.id = g1.groupId");
+ result.append(" WHERE g2.groupId IS NOT NULL");
+ result.append(") AS all_groups ON ");
+ result.append(groupStorageName);
+ result.append(".groupId = all_groups.parentId");
+
+ if (expandDevices) {
+ result.append(" INNER JOIN (");
+ result.append("SELECT groupId as parentId, id as deviceId FROM ");
+ result.append(getStorageName(Device.class));
+ result.append(" WHERE groupId IS NOT NULL");
+ result.append(") AS devices ON all_groups.groupId = devices.parentId");
+ }
+
+ result.append(" WHERE ");
+ result.append(conditionKey);
+ result.append(" = :");
+ result.append(conditionKey);
+
+ }
+
+ return result.toString();
+ }
+
+}
diff --git a/src/main/java/org/traccar/storage/MemoryStorage.java b/src/main/java/org/traccar/storage/MemoryStorage.java
new file mode 100644
index 000000000..f19897ff8
--- /dev/null
+++ b/src/main/java/org/traccar/storage/MemoryStorage.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.storage;
+
+import org.traccar.model.BaseModel;
+import org.traccar.model.Pair;
+import org.traccar.model.Permission;
+import org.traccar.storage.query.Request;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class MemoryStorage extends Storage {
+
+ private final Map<Pair<Class<?>, Class<?>>, Set<Pair<Long, Long>>> permissions = new HashMap<>();
+
+ @Override
+ public <T> List<T> getObjects(Class<T> clazz, Request request) {
+ return null;
+ }
+
+ @Override
+ public <T> long addObject(T entity, Request request) {
+ return 0;
+ }
+
+ @Override
+ public <T> void updateObject(T entity, Request request) {
+ }
+
+ @Override
+ public void removeObject(Class<?> clazz, Request request) {
+ }
+
+ private Set<Pair<Long, Long>> getPermissionsSet(Class<?> ownerClass, Class<?> propertyClass) {
+ return permissions.computeIfAbsent(new Pair<>(ownerClass, propertyClass), k -> new HashSet<>());
+ }
+
+ @Override
+ public List<Permission> getPermissions(
+ Class<? extends BaseModel> ownerClass, long ownerId,
+ Class<? extends BaseModel> propertyClass, long propertyId) {
+ return getPermissionsSet(ownerClass, propertyClass).stream()
+ .filter(pair -> ownerId == 0 || pair.getFirst().equals(ownerId))
+ .filter(pair -> propertyId == 0 || pair.getSecond().equals(propertyId))
+ .map(pair -> new Permission(ownerClass, pair.getFirst(), propertyClass, pair.getSecond()))
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public void addPermission(Permission permission) {
+ getPermissionsSet(permission.getOwnerClass(), permission.getPropertyClass())
+ .add(new Pair<>(permission.getOwnerId(), permission.getPropertyId()));
+ }
+
+ @Override
+ public void removePermission(Permission permission) {
+ getPermissionsSet(permission.getOwnerClass(), permission.getPropertyClass())
+ .remove(new Pair<>(permission.getOwnerId(), permission.getPropertyId()));
+ }
+
+}
diff --git a/src/main/java/org/traccar/database/QueryBuilder.java b/src/main/java/org/traccar/storage/QueryBuilder.java
index 396cbf562..2f4c07406 100644
--- a/src/main/java/org/traccar/database/QueryBuilder.java
+++ b/src/main/java/org/traccar/storage/QueryBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,12 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.database;
+package org.traccar.storage;
import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.traccar.Context;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
import org.traccar.model.Permission;
import javax.sql.DataSource;
@@ -33,7 +35,6 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.sql.Types;
-import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
@@ -41,17 +42,25 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+@SuppressWarnings("UnusedReturnValue")
public final class QueryBuilder {
private static final Logger LOGGER = LoggerFactory.getLogger(QueryBuilder.class);
+ private final Config config;
+ private final ObjectMapper objectMapper;
+
private final Map<String, List<Integer>> indexMap = new HashMap<>();
private Connection connection;
private PreparedStatement statement;
private final String query;
private final boolean returnGeneratedKeys;
- private QueryBuilder(DataSource dataSource, String query, boolean returnGeneratedKeys) throws SQLException {
+ private QueryBuilder(
+ Config config, DataSource dataSource, ObjectMapper objectMapper,
+ String query, boolean returnGeneratedKeys) throws SQLException {
+ this.config = config;
+ this.objectMapper = objectMapper;
this.query = query;
this.returnGeneratedKeys = returnGeneratedKeys;
if (query != null) {
@@ -126,13 +135,15 @@ public final class QueryBuilder {
return parsedQuery.toString();
}
- public static QueryBuilder create(DataSource dataSource, String query) throws SQLException {
- return new QueryBuilder(dataSource, query, false);
+ public static QueryBuilder create(
+ Config config, DataSource dataSource, ObjectMapper objectMapper, String query) throws SQLException {
+ return new QueryBuilder(config, dataSource, objectMapper, query, false);
}
public static QueryBuilder create(
- DataSource dataSource, String query, boolean returnGeneratedKeys) throws SQLException {
- return new QueryBuilder(dataSource, query, returnGeneratedKeys);
+ Config config, DataSource dataSource, ObjectMapper objectMapper, String query,
+ boolean returnGeneratedKeys) throws SQLException {
+ return new QueryBuilder(config, dataSource, objectMapper, query, returnGeneratedKeys);
}
private List<Integer> indexes(String name) {
@@ -255,36 +266,49 @@ public final class QueryBuilder {
return this;
}
- public QueryBuilder setObject(Object object) throws SQLException {
-
- Method[] methods = object.getClass().getMethods();
+ public QueryBuilder setValue(String name, Object value) throws SQLException {
+ if (value instanceof Boolean) {
+ setBoolean(name, (Boolean) value);
+ } else if (value instanceof Integer) {
+ setInteger(name, (Integer) value);
+ } else if (value instanceof Long) {
+ setLong(name, (Long) value);
+ } else if (value instanceof Double) {
+ setDouble(name, (Double) value);
+ } else if (value instanceof String) {
+ setString(name, (String) value);
+ } else if (value instanceof Date) {
+ setDate(name, (Date) value);
+ }
+ return this;
+ }
- for (Method method : methods) {
- if (method.getName().startsWith("get") && method.getParameterTypes().length == 0
- && !method.isAnnotationPresent(QueryIgnore.class)) {
- String name = method.getName().substring(3);
- try {
- if (method.getReturnType().equals(boolean.class)) {
- setBoolean(name, (Boolean) method.invoke(object));
- } else if (method.getReturnType().equals(int.class)) {
- setInteger(name, (Integer) method.invoke(object));
- } else if (method.getReturnType().equals(long.class)) {
- setLong(name, (Long) method.invoke(object), name.endsWith("Id"));
- } else if (method.getReturnType().equals(double.class)) {
- setDouble(name, (Double) method.invoke(object));
- } else if (method.getReturnType().equals(String.class)) {
- setString(name, (String) method.invoke(object));
- } else if (method.getReturnType().equals(Date.class)) {
- setDate(name, (Date) method.invoke(object));
- } else if (method.getReturnType().equals(byte[].class)) {
- setBlob(name, (byte[]) method.invoke(object));
- } else {
- setString(name, Context.getObjectMapper().writeValueAsString(method.invoke(object)));
- }
- } catch (IllegalAccessException | InvocationTargetException | JsonProcessingException error) {
- LOGGER.warn("Get property error", error);
+ public QueryBuilder setObject(Object object, List<String> columns) throws SQLException {
+
+ try {
+ for (String column : columns) {
+ Method method = object.getClass().getMethod(
+ "get" + Character.toUpperCase(column.charAt(0)) + column.substring(1));
+ if (method.getReturnType().equals(boolean.class)) {
+ setBoolean(column, (Boolean) method.invoke(object));
+ } else if (method.getReturnType().equals(int.class)) {
+ setInteger(column, (Integer) method.invoke(object));
+ } else if (method.getReturnType().equals(long.class)) {
+ setLong(column, (Long) method.invoke(object), column.endsWith("Id"));
+ } else if (method.getReturnType().equals(double.class)) {
+ setDouble(column, (Double) method.invoke(object));
+ } else if (method.getReturnType().equals(String.class)) {
+ setString(column, (String) method.invoke(object));
+ } else if (method.getReturnType().equals(Date.class)) {
+ setDate(column, (Date) method.invoke(object));
+ } else if (method.getReturnType().equals(byte[].class)) {
+ setBlob(column, (byte[]) method.invoke(object));
+ } else {
+ setString(column, objectMapper.writeValueAsString(method.invoke(object)));
}
}
+ } catch (ReflectiveOperationException | JsonProcessingException e) {
+ LOGGER.warn("Set object error", e);
}
return this;
@@ -294,15 +318,6 @@ public final class QueryBuilder {
void process(T object, ResultSet resultSet) throws SQLException;
}
- public <T> T executeQuerySingle(Class<T> clazz) throws SQLException {
- Collection<T> result = executeQuery(clazz);
- if (!result.isEmpty()) {
- return result.iterator().next();
- } else {
- return null;
- }
- }
-
private <T> void addProcessors(
List<ResultSetProcessor<T>> processors,
final Class<?> parameterType, final Method method, final String name) {
@@ -371,7 +386,7 @@ public final class QueryBuilder {
String value = resultSet.getString(name);
if (value != null && !value.isEmpty()) {
try {
- method.invoke(object, Context.getObjectMapper().readValue(value, parameterType));
+ method.invoke(object, objectMapper.readValue(value, parameterType));
} catch (InvocationTargetException | IllegalAccessException | IOException error) {
LOGGER.warn("Set property error", error);
}
@@ -380,13 +395,21 @@ public final class QueryBuilder {
}
}
- public <T> Collection<T> executeQuery(Class<T> clazz) throws SQLException {
+ private void logQuery() {
+ if (config.getBoolean(Keys.LOGGER_QUERIES)) {
+ LOGGER.info(query);
+ }
+ }
+
+ public <T> List<T> executeQuery(Class<T> clazz) throws SQLException {
List<T> result = new LinkedList<>();
if (query != null) {
try {
+ logQuery();
+
try (ResultSet resultSet = statement.executeQuery()) {
ResultSetMetaData resultMetaData = resultSet.getMetaData();
@@ -396,8 +419,7 @@ public final class QueryBuilder {
Method[] methods = clazz.getMethods();
for (final Method method : methods) {
- if (method.getName().startsWith("set") && method.getParameterTypes().length == 1
- && !method.isAnnotationPresent(QueryIgnore.class)) {
+ if (method.getName().startsWith("set") && method.getParameterTypes().length == 1) {
final String name = method.getName().substring(3);
@@ -443,6 +465,7 @@ public final class QueryBuilder {
if (query != null) {
try {
+ logQuery();
statement.execute();
if (returnGeneratedKeys) {
ResultSet resultSet = statement.getGeneratedKeys();
@@ -458,10 +481,11 @@ public final class QueryBuilder {
return 0;
}
- public Collection<Permission> executePermissionsQuery() throws SQLException, ClassNotFoundException {
+ public List<Permission> executePermissionsQuery() throws SQLException {
List<Permission> result = new LinkedList<>();
if (query != null) {
try {
+ logQuery();
try (ResultSet resultSet = statement.executeQuery()) {
ResultSetMetaData resultMetaData = resultSet.getMetaData();
while (resultSet.next()) {
diff --git a/src/main/java/org/traccar/database/QueryIgnore.java b/src/main/java/org/traccar/storage/QueryIgnore.java
index ac835cf2f..553d4b9fc 100644
--- a/src/main/java/org/traccar/database/QueryIgnore.java
+++ b/src/main/java/org/traccar/storage/QueryIgnore.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.database;
+package org.traccar.storage;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
diff --git a/src/main/java/org/traccar/storage/Storage.java b/src/main/java/org/traccar/storage/Storage.java
new file mode 100644
index 000000000..55f5c22c0
--- /dev/null
+++ b/src/main/java/org/traccar/storage/Storage.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.storage;
+
+import org.traccar.model.BaseModel;
+import org.traccar.model.Permission;
+import org.traccar.storage.query.Request;
+
+import java.util.List;
+
+public abstract class Storage {
+
+ public abstract <T> List<T> getObjects(Class<T> clazz, Request request) throws StorageException;
+
+ public abstract <T> long addObject(T entity, Request request) throws StorageException;
+
+ public abstract <T> void updateObject(T entity, Request request) throws StorageException;
+
+ public abstract void removeObject(Class<?> clazz, Request request) throws StorageException;
+
+ public abstract List<Permission> getPermissions(
+ Class<? extends BaseModel> ownerClass, long ownerId,
+ Class<? extends BaseModel> propertyClass, long propertyId) throws StorageException;
+
+ public abstract void addPermission(Permission permission) throws StorageException;
+
+ public abstract void removePermission(Permission permission) throws StorageException;
+
+ public List<Permission> getPermissions(
+ Class<? extends BaseModel> ownerClass,
+ Class<? extends BaseModel> propertyClass) throws StorageException {
+ return getPermissions(ownerClass, 0, propertyClass, 0);
+ }
+
+ public <T> T getObject(Class<T> clazz, Request request) throws StorageException {
+ var objects = getObjects(clazz, request);
+ return objects.isEmpty() ? null : objects.get(0);
+ }
+
+}
diff --git a/src/main/java/org/traccar/storage/StorageException.java b/src/main/java/org/traccar/storage/StorageException.java
new file mode 100644
index 000000000..3f066cae6
--- /dev/null
+++ b/src/main/java/org/traccar/storage/StorageException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.storage;
+
+public class StorageException extends Exception {
+
+ public StorageException(String message) {
+ super(message);
+ }
+
+ public StorageException(Throwable cause) {
+ super(cause);
+ }
+
+ public StorageException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/src/main/java/org/traccar/database/QueryExtended.java b/src/main/java/org/traccar/storage/StorageName.java
index 07bc2c211..bf824c333 100644
--- a/src/main/java/org/traccar/database/QueryExtended.java
+++ b/src/main/java/org/traccar/storage/StorageName.java
@@ -1,6 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
- * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.database;
+package org.traccar.storage;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-@Target(ElementType.METHOD)
+@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
-public @interface QueryExtended {
+public @interface StorageName {
+ String value();
}
diff --git a/src/main/java/org/traccar/storage/query/Columns.java b/src/main/java/org/traccar/storage/query/Columns.java
new file mode 100644
index 000000000..a00400b36
--- /dev/null
+++ b/src/main/java/org/traccar/storage/query/Columns.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.storage.query;
+
+import org.traccar.storage.QueryIgnore;
+
+import java.beans.Introspector;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public abstract class Columns {
+
+ public abstract List<String> getColumns(Class<?> clazz, String type);
+
+ protected List<String> getAllColumns(Class<?> clazz, String type) {
+ List<String> columns = new LinkedList<>();
+ Method[] methods = clazz.getMethods();
+ for (Method method : methods) {
+ int parameterCount = type.equals("set") ? 1 : 0;
+ if (method.getName().startsWith(type) && method.getParameterTypes().length == parameterCount
+ && !method.isAnnotationPresent(QueryIgnore.class)
+ && !method.getName().equals("getClass")) {
+ columns.add(Introspector.decapitalize(method.getName().substring(3)));
+ }
+ }
+ return columns;
+ }
+
+ public static class All extends Columns {
+ @Override
+ public List<String> getColumns(Class<?> clazz, String type) {
+ return getAllColumns(clazz, type);
+ }
+ }
+
+ public static class Include extends Columns {
+ private final List<String> columns;
+
+ public Include(String... columns) {
+ this.columns = Arrays.stream(columns).collect(Collectors.toList());
+ }
+
+ @Override
+ public List<String> getColumns(Class<?> clazz, String type) {
+ return columns;
+ }
+ }
+
+ public static class Exclude extends Columns {
+ private final Set<String> columns;
+
+ public Exclude(String... columns) {
+ this.columns = Arrays.stream(columns).collect(Collectors.toSet());
+ }
+
+ @Override
+ public List<String> getColumns(Class<?> clazz, String type) {
+ return getAllColumns(clazz, type).stream()
+ .filter(column -> !columns.contains(column))
+ .collect(Collectors.toList());
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/storage/query/Condition.java b/src/main/java/org/traccar/storage/query/Condition.java
new file mode 100644
index 000000000..08b199052
--- /dev/null
+++ b/src/main/java/org/traccar/storage/query/Condition.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.storage.query;
+
+import org.traccar.model.GroupedModel;
+
+import java.util.List;
+
+public interface Condition {
+
+ static Condition merge(List<Condition> conditions) {
+ Condition result = null;
+ var iterator = conditions.iterator();
+ if (iterator.hasNext()) {
+ result = iterator.next();
+ while (iterator.hasNext()) {
+ result = new Condition.And(result, iterator.next());
+ }
+ }
+ return result;
+ }
+
+ class Equals extends Compare {
+ public Equals(String column, Object value) {
+ super(column, "=", column, value);
+ }
+ }
+
+ class Compare implements Condition {
+ private final String column;
+ private final String operator;
+ private final String variable;
+ private final Object value;
+
+ public Compare(String column, String operator, String variable, Object value) {
+ this.column = column;
+ this.operator = operator;
+ this.variable = variable;
+ this.value = value;
+ }
+
+ public String getColumn() {
+ return column;
+ }
+
+ public String getOperator() {
+ return operator;
+ }
+
+ public String getVariable() {
+ return variable;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+ }
+
+ class Between implements Condition {
+ private final String column;
+ private final String fromVariable;
+ private final Object fromValue;
+ private final String toVariable;
+ private final Object toValue;
+
+ public Between(String column, String fromVariable, Object fromValue, String toVariable, Object toValue) {
+ this.column = column;
+ this.fromVariable = fromVariable;
+ this.fromValue = fromValue;
+ this.toVariable = toVariable;
+ this.toValue = toValue;
+ }
+
+ public String getColumn() {
+ return column;
+ }
+
+ public String getFromVariable() {
+ return fromVariable;
+ }
+
+ public Object getFromValue() {
+ return fromValue;
+ }
+
+ public String getToVariable() {
+ return toVariable;
+ }
+
+ public Object getToValue() {
+ return toValue;
+ }
+ }
+
+ class Or extends Binary {
+ public Or(Condition first, Condition second) {
+ super(first, second, "OR");
+ }
+ }
+
+ class And extends Binary {
+ public And(Condition first, Condition second) {
+ super(first, second, "AND");
+ }
+ }
+
+ class Binary implements Condition {
+ private final Condition first;
+ private final Condition second;
+ private final String operator;
+
+ public Binary(Condition first, Condition second, String operator) {
+ this.first = first;
+ this.second = second;
+ this.operator = operator;
+ }
+
+ public Condition getFirst() {
+ return first;
+ }
+
+ public Condition getSecond() {
+ return second;
+ }
+
+ public String getOperator() {
+ return operator;
+ }
+ }
+
+ class Permission implements Condition {
+ private final Class<?> ownerClass;
+ private final long ownerId;
+ private final Class<?> propertyClass;
+ private final long propertyId;
+ private final boolean excludeGroups;
+
+ private Permission(
+ Class<?> ownerClass, long ownerId, Class<?> propertyClass, long propertyId, boolean excludeGroups) {
+ this.ownerClass = ownerClass;
+ this.ownerId = ownerId;
+ this.propertyClass = propertyClass;
+ this.propertyId = propertyId;
+ this.excludeGroups = excludeGroups;
+ }
+
+ public Permission(Class<?> ownerClass, long ownerId, Class<?> propertyClass) {
+ this(ownerClass, ownerId, propertyClass, 0, false);
+ }
+
+ public Permission(Class<?> ownerClass, Class<?> propertyClass, long propertyId) {
+ this(ownerClass, 0, propertyClass, propertyId, false);
+ }
+
+ public Permission excludeGroups() {
+ return new Permission(this.ownerClass, this.ownerId, this.propertyClass, this.propertyId, true);
+ }
+
+ public Class<?> getOwnerClass() {
+ return ownerClass;
+ }
+
+ public long getOwnerId() {
+ return ownerId;
+ }
+
+ public Class<?> getPropertyClass() {
+ return propertyClass;
+ }
+
+ public long getPropertyId() {
+ return propertyId;
+ }
+
+ public boolean getIncludeGroups() {
+ boolean ownerGroupModel = GroupedModel.class.isAssignableFrom(ownerClass);
+ boolean propertyGroupModel = GroupedModel.class.isAssignableFrom(propertyClass);
+ return (ownerGroupModel || propertyGroupModel) && !excludeGroups;
+ }
+ }
+
+ class LatestPositions implements Condition {
+ private final long deviceId;
+
+ public LatestPositions(long deviceId) {
+ this.deviceId = deviceId;
+ }
+
+ public LatestPositions() {
+ this(0);
+ }
+
+ public long getDeviceId() {
+ return deviceId;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/database/OrderManager.java b/src/main/java/org/traccar/storage/query/Limit.java
index c3253e52f..9673e5426 100644
--- a/src/main/java/org/traccar/database/OrderManager.java
+++ b/src/main/java/org/traccar/storage/query/Limit.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,14 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.database;
+package org.traccar.storage.query;
-import org.traccar.model.Order;
+public class Limit {
-public class OrderManager extends ExtendedObjectManager<Order> {
+ private final int value;
- public OrderManager(DataManager dataManager) {
- super(dataManager, Order.class);
+ public Limit(int value) {
+ this.value = value;
+ }
+
+ public int getValue() {
+ return value;
}
}
diff --git a/src/main/java/org/traccar/storage/query/Order.java b/src/main/java/org/traccar/storage/query/Order.java
new file mode 100644
index 000000000..f10970381
--- /dev/null
+++ b/src/main/java/org/traccar/storage/query/Order.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.storage.query;
+
+public class Order {
+
+ private final String column;
+ private final boolean descending;
+ private final int limit;
+
+ public Order(String column) {
+ this(column, false, 0);
+ }
+
+ public Order(String column, boolean descending, int limit) {
+ this.column = column;
+ this.descending = descending;
+ this.limit = limit;
+ }
+
+ public String getColumn() {
+ return column;
+ }
+
+ public boolean getDescending() {
+ return descending;
+ }
+
+ public int getLimit() {
+ return limit;
+ }
+
+}
diff --git a/src/main/java/org/traccar/storage/query/Request.java b/src/main/java/org/traccar/storage/query/Request.java
new file mode 100644
index 000000000..b9c2c963c
--- /dev/null
+++ b/src/main/java/org/traccar/storage/query/Request.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.storage.query;
+
+public class Request {
+
+ private final Columns columns;
+ private final Condition condition;
+ private final Order order;
+
+ public Request(Columns columns) {
+ this(columns, null, null);
+ }
+
+ public Request(Condition condition) {
+ this(null, condition, null);
+ }
+
+ public Request(Columns columns, Condition condition) {
+ this(columns, condition, null);
+ }
+
+ public Request(Columns columns, Order order) {
+ this(columns, null, order);
+ }
+
+ public Request(Columns columns, Condition condition, Order order) {
+ this.columns = columns;
+ this.condition = condition;
+ this.order = order;
+ }
+
+ public Columns getColumns() {
+ return columns;
+ }
+
+ public Condition getCondition() {
+ return condition;
+ }
+
+ public Order getOrder() {
+ return order;
+ }
+
+}
diff --git a/src/main/java/org/traccar/web/ConsoleServlet.java b/src/main/java/org/traccar/web/ConsoleServlet.java
index 0f3dcd8fd..902a4f7a9 100644
--- a/src/main/java/org/traccar/web/ConsoleServlet.java
+++ b/src/main/java/org/traccar/web/ConsoleServlet.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,7 +19,7 @@ import org.h2.server.web.ConnectionInfo;
import org.h2.server.web.WebServlet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.traccar.Context;
+import org.traccar.config.Config;
import org.traccar.config.Keys;
import java.lang.reflect.Field;
@@ -30,6 +30,12 @@ public class ConsoleServlet extends WebServlet {
private static final Logger LOGGER = LoggerFactory.getLogger(ConsoleServlet.class);
+ private final Config config;
+
+ public ConsoleServlet(Config config) {
+ this.config = config;
+ }
+
@Override
public void init() {
super.init();
@@ -40,9 +46,9 @@ public class ConsoleServlet extends WebServlet {
org.h2.server.web.WebServer server = (org.h2.server.web.WebServer) field.get(this);
ConnectionInfo connectionInfo = new ConnectionInfo("Traccar|"
- + Context.getConfig().getString(Keys.DATABASE_DRIVER) + "|"
- + Context.getConfig().getString(Keys.DATABASE_URL) + "|"
- + Context.getConfig().getString(Keys.DATABASE_USER));
+ + config.getString(Keys.DATABASE_DRIVER) + "|"
+ + config.getString(Keys.DATABASE_URL) + "|"
+ + config.getString(Keys.DATABASE_USER));
Method method;
diff --git a/src/main/java/org/traccar/web/ThrottlingFilter.java b/src/main/java/org/traccar/web/ThrottlingFilter.java
new file mode 100644
index 000000000..054af652f
--- /dev/null
+++ b/src/main/java/org/traccar/web/ThrottlingFilter.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.web;
+
+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;
+
+@Singleton
+public class ThrottlingFilter extends DoSFilter {
+
+ @Inject
+ private Config config;
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ super.init(filterConfig);
+ if (config.hasKey(Keys.WEB_MAX_REQUESTS_PER_SECOND)) {
+ setMaxRequestsPerSec(config.getInteger(Keys.WEB_MAX_REQUESTS_PER_SECOND));
+ }
+ }
+
+ @Override
+ protected String extractUserId(ServletRequest request) {
+ HttpSession session = ((HttpServletRequest) request).getSession(false);
+ if (session != null) {
+ var userId = session.getAttribute("userId");
+ return userId != null ? userId.toString() : null;
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/org/traccar/web/WebInjectionManagerFactory.java b/src/main/java/org/traccar/web/WebInjectionManagerFactory.java
new file mode 100644
index 000000000..14d9d3dbc
--- /dev/null
+++ b/src/main/java/org/traccar/web/WebInjectionManagerFactory.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.web;
+
+import org.glassfish.hk2.api.ServiceLocator;
+import org.glassfish.jersey.inject.hk2.Hk2InjectionManagerFactory;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.inject.InjectionManagerFactory;
+import org.jvnet.hk2.guice.bridge.api.GuiceBridge;
+import org.jvnet.hk2.guice.bridge.api.GuiceIntoHK2Bridge;
+import org.traccar.Main;
+
+import javax.annotation.Priority;
+
+@Priority(20)
+public class WebInjectionManagerFactory implements InjectionManagerFactory {
+
+ private final InjectionManagerFactory originalFactory = new Hk2InjectionManagerFactory();
+
+ private InjectionManager injectGuiceBridge(InjectionManager injectionManager) {
+ var serviceLocator = injectionManager.getInstance(ServiceLocator.class);
+ GuiceBridge.getGuiceBridge().initializeGuiceBridge(serviceLocator);
+ var guiceBridge = serviceLocator.getService(GuiceIntoHK2Bridge.class);
+ guiceBridge.bridgeGuiceInjector(Main.getInjector());
+ return injectionManager;
+ }
+
+ @Override
+ public InjectionManager create() {
+ return injectGuiceBridge(originalFactory.create());
+ }
+
+ @Override
+ public InjectionManager create(Object parent) {
+ return injectGuiceBridge(originalFactory.create(parent));
+ }
+}
diff --git a/src/main/java/org/traccar/web/WebModule.java b/src/main/java/org/traccar/web/WebModule.java
new file mode 100644
index 000000000..0722c5d1e
--- /dev/null
+++ b/src/main/java/org/traccar/web/WebModule.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.web;
+
+import com.google.inject.servlet.ServletModule;
+import org.traccar.api.AsyncSocketServlet;
+import org.traccar.api.MediaFilter;
+
+public class WebModule extends ServletModule {
+
+ @Override
+ protected void configureServlets() {
+ filter("/api/*").through(ThrottlingFilter.class);
+ filter("/api/media/*").through(MediaFilter.class);
+ serve("/api/socket").with(AsyncSocketServlet.class);
+ }
+}
diff --git a/src/main/java/org/traccar/web/WebServer.java b/src/main/java/org/traccar/web/WebServer.java
index 604edfedc..79d19cc9b 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 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,8 @@
*/
package org.traccar.web;
+import com.google.inject.Injector;
+import com.google.inject.servlet.GuiceFilter;
import org.eclipse.jetty.http.HttpCookie;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
@@ -40,37 +42,42 @@ import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.servlet.ServletContainer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.traccar.Context;
-import org.traccar.api.DateParameterConverterProvider;
-import org.traccar.config.Config;
-import org.traccar.api.AsyncSocketServlet;
+import org.traccar.LifecycleObject;
import org.traccar.api.CorsResponseFilter;
-import org.traccar.api.MediaFilter;
-import org.traccar.api.ObjectMapperProvider;
+import org.traccar.api.DateParameterConverterProvider;
import org.traccar.api.ResourceErrorHandler;
-import org.traccar.api.SecurityRequestFilter;
import org.traccar.api.resource.ServerResource;
+import org.traccar.api.security.SecurityRequestFilter;
+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 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 {
+public class WebServer implements LifecycleObject {
private static final Logger LOGGER = LoggerFactory.getLogger(WebServer.class);
- private Server server;
-
- private void initServer(Config config) {
+ private final Injector injector;
+ private final Config config;
+ private final Server server;
+ public WebServer(Injector injector, Config config) {
+ this.injector = injector;
+ this.config = config;
String address = config.getString(Keys.WEB_ADDRESS);
int port = config.getInteger(Keys.WEB_PORT);
if (address == null) {
@@ -78,39 +85,42 @@ public class WebServer {
} else {
server = new Server(new InetSocketAddress(address, port));
}
- }
-
- public WebServer(Config config) {
-
- initServer(config);
ServletContextHandler servletHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
+ JettyWebSocketServletContainerInitializer.configure(servletHandler, null);
+ servletHandler.addFilter(GuiceFilter.class, "/*", EnumSet.allOf(DispatcherType.class));
- initApi(config, servletHandler);
- initSessionConfig(config, servletHandler);
+ initApi(servletHandler);
+ initSessionConfig(servletHandler);
if (config.getBoolean(Keys.WEB_CONSOLE)) {
- servletHandler.addServlet(new ServletHolder(new ConsoleServlet()), "/console/*");
+ servletHandler.addServlet(new ServletHolder(new ConsoleServlet(config)), "/console/*");
}
- initWebApp(config, servletHandler);
+ initWebApp(servletHandler);
servletHandler.setErrorHandler(new ErrorHandler() {
@Override
protected void handleErrorPage(
HttpServletRequest request, Writer writer, int code, String message) throws IOException {
- writer.write("<!DOCTYPE><html><head><title>Error</title></head><html><body>"
- + code + " - " + HttpStatus.getMessage(code) + "</body></html>");
+ 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>");
+ }
}
});
HandlerList handlers = new HandlerList();
- initClientProxy(config, handlers);
+ initClientProxy(handlers);
handlers.addHandler(servletHandler);
handlers.addHandler(new GzipHandler());
server.setHandler(handlers);
- if (config.getBoolean(Keys.WEB_REQUEST_LOG_ENABLE)) {
+ if (config.hasKey(Keys.WEB_REQUEST_LOG_PATH)) {
RequestLogWriter logWriter = new RequestLogWriter(config.getString(Keys.WEB_REQUEST_LOG_PATH));
logWriter.setAppend(true);
logWriter.setRetainDays(config.getInteger(Keys.WEB_REQUEST_LOG_RETAIN_DAYS));
@@ -119,7 +129,7 @@ public class WebServer {
}
}
- private void initClientProxy(Config config, HandlerList handlers) {
+ private void initClientProxy(HandlerList handlers) {
int port = config.getInteger(Keys.PROTOCOL_PORT.withPrefix("osmand"));
if (port != 0) {
ServletContextHandler servletHandler = new ServletContextHandler() {
@@ -132,14 +142,14 @@ public class WebServer {
}
}
};
- ServletHolder servletHolder = new ServletHolder(new AsyncProxyServlet.Transparent());
+ ServletHolder servletHolder = new ServletHolder(AsyncProxyServlet.Transparent.class);
servletHolder.setInitParameter("proxyTo", "http://localhost:" + port);
servletHandler.addServlet(servletHolder, "/");
handlers.addHandler(servletHandler);
}
}
- private void initWebApp(Config config, ServletContextHandler servletHandler) {
+ private void initWebApp(ServletContextHandler servletHandler) {
ServletHolder servletHolder = new ServletHolder(DefaultServlet.class);
servletHolder.setInitParameter("resourceBase", new File(config.getString(Keys.WEB_PATH)).getAbsolutePath());
servletHolder.setInitParameter("dirAllowed", "false");
@@ -155,10 +165,7 @@ public class WebServer {
servletHandler.addServlet(servletHolder, "/*");
}
- private void initApi(Config config, ServletContextHandler servletHandler) {
- servletHandler.addServlet(new ServletHolder(new AsyncSocketServlet()), "/api/socket");
- JettyWebSocketServletContainerInitializer.configure(servletHandler, null);
-
+ private void initApi(ServletContextHandler servletHandler) {
String mediaPath = config.getString(Keys.MEDIA_PATH);
if (mediaPath != null) {
ServletHolder servletHolder = new ServletHolder(DefaultServlet.class);
@@ -166,21 +173,27 @@ public class WebServer {
servletHolder.setInitParameter("dirAllowed", "false");
servletHolder.setInitParameter("pathInfoOnly", "true");
servletHandler.addServlet(servletHolder, "/api/media/*");
- servletHandler.addFilter(MediaFilter.class, "/api/media/*", EnumSet.allOf(DispatcherType.class));
}
ResourceConfig resourceConfig = new ResourceConfig();
resourceConfig.registerClasses(
- JacksonFeature.class, ObjectMapperProvider.class, ResourceErrorHandler.class,
- SecurityRequestFilter.class, CorsResponseFilter.class, DateParameterConverterProvider.class);
+ JacksonFeature.class,
+ ObjectMapperContextResolver.class,
+ DateParameterConverterProvider.class,
+ SecurityRequestFilter.class,
+ CorsResponseFilter.class,
+ ResourceErrorHandler.class);
resourceConfig.packages(ServerResource.class.getPackage().getName());
+ if (resourceConfig.getClasses().stream().filter(ServerResource.class::equals).findAny().isEmpty()) {
+ LOGGER.warn("Failed to load API resources");
+ }
servletHandler.addServlet(new ServletHolder(new ServletContainer(resourceConfig)), "/api/*");
}
- private void initSessionConfig(Config config, ServletContextHandler servletHandler) {
+ private void initSessionConfig(ServletContextHandler servletHandler) {
if (config.getBoolean(Keys.WEB_PERSIST_SESSION)) {
DatabaseAdaptor databaseAdaptor = new DatabaseAdaptor();
- databaseAdaptor.setDatasource(Context.getDataManager().getDataSource());
+ databaseAdaptor.setDatasource(injector.getInstance(DataSource.class));
JDBCSessionDataStoreFactory jdbcSessionDataStoreFactory = new JDBCSessionDataStoreFactory();
jdbcSessionDataStoreFactory.setDatabaseAdaptor(databaseAdaptor);
SessionHandler sessionHandler = servletHandler.getSessionHandler();
@@ -214,20 +227,14 @@ public class WebServer {
}
}
- public void start() {
- try {
- server.start();
- } catch (Exception error) {
- LOGGER.warn("Web server start failed", error);
- }
+ @Override
+ public void start() throws Exception {
+ server.start();
}
- public void stop() {
- try {
- server.stop();
- } catch (Exception error) {
- LOGGER.warn("Web server stop failed", error);
- }
+ @Override
+ public void stop() throws Exception {
+ server.stop();
}
}
diff --git a/src/main/resources/META-INF/services/org.glassfish.jersey.internal.inject.InjectionManagerFactory b/src/main/resources/META-INF/services/org.glassfish.jersey.internal.inject.InjectionManagerFactory
new file mode 100644
index 000000000..ce0a70630
--- /dev/null
+++ b/src/main/resources/META-INF/services/org.glassfish.jersey.internal.inject.InjectionManagerFactory
@@ -0,0 +1 @@
+org.traccar.web.WebInjectionManagerFactory